- claude-code 선택적 탐색 흐름을 참고해 Cowork/Code 시스템 프롬프트에서 folder_map 상시 선행 지시를 완화하고 glob/grep 기반 좁은 탐색을 우선하도록 조정함 - FolderMapTool 기본 depth를 2로, include_files 기본값을 false로 낮추고 MultiReadTool 최대 파일 수를 8개로 줄여 초기 과탐색 폭을 보수적으로 조정함 - AgentLoopExplorationPolicy partial을 추가해 탐색 범위 분류, broad-scan corrective hint, exploration_breadth 성능 로그를 연결함 - AgentLoopService에 탐색 범위 가이드 주입과 실행 중 탐색 폭 추적을 추가하고, 좁은 질문에서 반복적인 folder_map/대량 multi_read를 교정하도록 정리함 - DocxToHtmlConverter nullable 경고를 수정해 Release 빌드 경고 0 / 오류 0 기준을 다시 충족함 - README와 docs/DEVELOPMENT.md에 2026-04-09 10:36 (KST) 기준 개발 이력을 반영함
This commit is contained in:
@@ -162,22 +162,22 @@ public partial class LlmService
|
||||
var root = doc.RootElement;
|
||||
|
||||
// 토큰 사용량
|
||||
if (root.TryGetProperty("usage", out var usage))
|
||||
if (root.SafeTryGetProperty("usage", out var usage))
|
||||
TryParseSigmoidUsageFromElement(usage);
|
||||
|
||||
// 컨텐츠 블록 파싱
|
||||
var blocks = new List<ContentBlock>();
|
||||
if (root.TryGetProperty("content", out var content))
|
||||
if (root.SafeTryGetProperty("content", out var content))
|
||||
{
|
||||
foreach (var block in content.EnumerateArray())
|
||||
{
|
||||
var type = block.TryGetProperty("type", out var tp) ? tp.GetString() : "";
|
||||
var type = block.SafeTryGetProperty("type", out var tp) ? tp.SafeGetString() : "";
|
||||
if (type == "text")
|
||||
{
|
||||
blocks.Add(new ContentBlock
|
||||
{
|
||||
Type = "text",
|
||||
Text = block.TryGetProperty("text", out var txt) ? txt.GetString() ?? "" : ""
|
||||
Text = block.SafeTryGetProperty("text", out var txt) ? txt.SafeGetString() ?? "" : ""
|
||||
});
|
||||
}
|
||||
else if (type == "tool_use")
|
||||
@@ -185,9 +185,9 @@ public partial class LlmService
|
||||
blocks.Add(new ContentBlock
|
||||
{
|
||||
Type = "tool_use",
|
||||
ToolName = block.TryGetProperty("name", out var nm) ? nm.GetString() ?? "" : "",
|
||||
ToolId = block.TryGetProperty("id", out var bid) ? bid.GetString() ?? "" : "",
|
||||
ToolInput = block.TryGetProperty("input", out var inp) ? inp.Clone() : null
|
||||
ToolName = block.SafeTryGetProperty("name", out var nm) ? nm.SafeGetString() ?? "" : "",
|
||||
ToolId = block.SafeTryGetProperty("id", out var bid) ? bid.SafeGetString() ?? "" : "",
|
||||
ToolInput = block.SafeTryGetProperty("input", out var inp) ? inp.Clone() : null
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -220,8 +220,8 @@ public partial class LlmService
|
||||
new
|
||||
{
|
||||
type = "tool_result",
|
||||
tool_use_id = root.TryGetProperty("tool_use_id", out var tuid) ? tuid.GetString() : "",
|
||||
content = root.TryGetProperty("content", out var tcont) ? tcont.GetString() : ""
|
||||
tool_use_id = root.SafeTryGetProperty("tool_use_id", out var tuid) ? tuid.SafeGetString() : "",
|
||||
content = root.SafeTryGetProperty("content", out var tcont) ? tcont.SafeGetString() : ""
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -236,20 +236,20 @@ public partial class LlmService
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(m.Content);
|
||||
if (!doc.RootElement.TryGetProperty("_tool_use_blocks", out var blocksArr)) throw new Exception();
|
||||
if (!doc.RootElement.SafeTryGetProperty("_tool_use_blocks", out var blocksArr)) throw new Exception();
|
||||
var contentList = new List<object>();
|
||||
foreach (var b in blocksArr.EnumerateArray())
|
||||
{
|
||||
var bType = b.TryGetProperty("type", out var bt) ? bt.GetString() : "";
|
||||
var bType = b.SafeTryGetProperty("type", out var bt) ? bt.SafeGetString() : "";
|
||||
if (bType == "text")
|
||||
contentList.Add(new { type = "text", text = b.TryGetProperty("text", out var tx) ? tx.GetString() ?? "" : "" });
|
||||
contentList.Add(new { type = "text", text = b.SafeTryGetProperty("text", out var tx) ? tx.SafeGetString() ?? "" : "" });
|
||||
else if (bType == "tool_use")
|
||||
contentList.Add(new
|
||||
{
|
||||
type = "tool_use",
|
||||
id = b.TryGetProperty("id", out var bid) ? bid.GetString() ?? "" : "",
|
||||
name = b.TryGetProperty("name", out var nm) ? nm.GetString() ?? "" : "",
|
||||
input = b.TryGetProperty("input", out var inp) ? (object)inp.Clone() : new { }
|
||||
id = b.SafeTryGetProperty("id", out var bid) ? bid.SafeGetString() ?? "" : "",
|
||||
name = b.SafeTryGetProperty("name", out var nm) ? nm.SafeGetString() ?? "" : "",
|
||||
input = b.SafeTryGetProperty("input", out var inp) ? (object)inp.Clone() : new { }
|
||||
});
|
||||
}
|
||||
msgs.Add(new { role = "assistant", content = contentList });
|
||||
@@ -347,26 +347,26 @@ public partial class LlmService
|
||||
TryParseGeminiUsage(root);
|
||||
|
||||
var blocks = new List<ContentBlock>();
|
||||
if (root.TryGetProperty("candidates", out var candidates) && candidates.GetArrayLength() > 0)
|
||||
if (root.SafeTryGetProperty("candidates", out var candidates) && candidates.GetArrayLength() > 0)
|
||||
{
|
||||
var firstCandidate = candidates[0];
|
||||
if (firstCandidate.TryGetProperty("content", out var contentObj) &&
|
||||
contentObj.TryGetProperty("parts", out var parts))
|
||||
if (firstCandidate.SafeTryGetProperty("content", out var contentObj) &&
|
||||
contentObj.SafeTryGetProperty("parts", out var parts))
|
||||
{
|
||||
foreach (var part in parts.EnumerateArray())
|
||||
{
|
||||
if (part.TryGetProperty("text", out var text))
|
||||
if (part.SafeTryGetProperty("text", out var text))
|
||||
{
|
||||
blocks.Add(new ContentBlock { Type = "text", Text = text.GetString() ?? "" });
|
||||
blocks.Add(new ContentBlock { Type = "text", Text = text.SafeGetString() ?? "" });
|
||||
}
|
||||
else if (part.TryGetProperty("functionCall", out var fc))
|
||||
else if (part.SafeTryGetProperty("functionCall", out var fc))
|
||||
{
|
||||
blocks.Add(new ContentBlock
|
||||
{
|
||||
Type = "tool_use",
|
||||
ToolName = fc.TryGetProperty("name", out var fcName) ? fcName.GetString() ?? "" : "",
|
||||
ToolName = fc.SafeTryGetProperty("name", out var fcName) ? fcName.SafeGetString() ?? "" : "",
|
||||
ToolId = Guid.NewGuid().ToString("N")[..12],
|
||||
ToolInput = fc.TryGetProperty("args", out var a) ? a.Clone() : null
|
||||
ToolInput = fc.SafeTryGetProperty("args", out var a) ? a.Clone() : null
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -391,8 +391,8 @@ public partial class LlmService
|
||||
{
|
||||
using var doc = JsonDocument.Parse(m.Content);
|
||||
var root = doc.RootElement;
|
||||
var toolName = root.TryGetProperty("tool_name", out var tn) ? tn.GetString() ?? "" : "";
|
||||
var toolContent = root.TryGetProperty("content", out var tc) ? tc.GetString() ?? "" : "";
|
||||
var toolName = root.SafeTryGetProperty("tool_name", out var tn) ? tn.SafeGetString() ?? "" : "";
|
||||
var toolContent = root.SafeTryGetProperty("content", out var tc) ? tc.SafeGetString() ?? "" : "";
|
||||
contents.Add(new
|
||||
{
|
||||
role = "function",
|
||||
@@ -419,21 +419,21 @@ public partial class LlmService
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(m.Content);
|
||||
if (doc.RootElement.TryGetProperty("_tool_use_blocks", out var blocksArr))
|
||||
if (doc.RootElement.SafeTryGetProperty("_tool_use_blocks", out var blocksArr))
|
||||
{
|
||||
var parts = new List<object>();
|
||||
foreach (var b in blocksArr.EnumerateArray())
|
||||
{
|
||||
var bType = b.TryGetProperty("type", out var bt) ? bt.GetString() : "";
|
||||
var bType = b.SafeTryGetProperty("type", out var bt) ? bt.SafeGetString() : "";
|
||||
if (bType == "text")
|
||||
parts.Add(new { text = b.TryGetProperty("text", out var tx) ? tx.GetString() ?? "" : "" });
|
||||
parts.Add(new { text = b.SafeTryGetProperty("text", out var tx) ? tx.SafeGetString() ?? "" : "" });
|
||||
else if (bType == "tool_use")
|
||||
parts.Add(new
|
||||
{
|
||||
functionCall = new
|
||||
{
|
||||
name = b.TryGetProperty("name", out var nm) ? nm.GetString() ?? "" : "",
|
||||
args = b.TryGetProperty("input", out var inp) ? (object)inp.Clone() : new { }
|
||||
name = b.SafeTryGetProperty("name", out var nm) ? nm.SafeGetString() ?? "" : "",
|
||||
args = b.SafeTryGetProperty("input", out var inp) ? (object)inp.Clone() : new { }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -639,13 +639,13 @@ public partial class LlmService
|
||||
json = json[braceStart..(braceEnd + 1)];
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
var name = root.TryGetProperty("name", out var n) ? n.GetString() ?? "" : "";
|
||||
var name = root.SafeTryGetProperty("name", out var n) ? n.SafeGetString() ?? "" : "";
|
||||
if (string.IsNullOrEmpty(name)) return null;
|
||||
|
||||
JsonElement? args = null;
|
||||
if (root.TryGetProperty("arguments", out var a))
|
||||
if (root.SafeTryGetProperty("arguments", out var a))
|
||||
args = a.Clone();
|
||||
else if (root.TryGetProperty("parameters", out var p))
|
||||
else if (root.SafeTryGetProperty("parameters", out var p))
|
||||
args = p.Clone();
|
||||
|
||||
return new ContentBlock
|
||||
@@ -702,8 +702,8 @@ public partial class LlmService
|
||||
msgs.Add(new
|
||||
{
|
||||
role = "tool",
|
||||
tool_call_id = root.GetProperty("tool_use_id").GetString(),
|
||||
content = root.GetProperty("content").GetString(),
|
||||
tool_call_id = root.GetProperty("tool_use_id").SafeGetString(),
|
||||
content = root.GetProperty("content").SafeGetString(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -721,19 +721,19 @@ public partial class LlmService
|
||||
var toolCallsList = new List<object>();
|
||||
foreach (var b in blocksArr.EnumerateArray())
|
||||
{
|
||||
var bType = b.GetProperty("type").GetString();
|
||||
var bType = b.GetProperty("type").SafeGetString();
|
||||
if (bType == "text")
|
||||
textContent = b.GetProperty("text").GetString() ?? "";
|
||||
textContent = b.GetProperty("text").SafeGetString() ?? "";
|
||||
else if (bType == "tool_use")
|
||||
{
|
||||
var argsJson = b.TryGetProperty("input", out var inp) ? inp.GetRawText() : "{}";
|
||||
var argsJson = b.SafeTryGetProperty("input", out var inp) ? inp.GetRawText() : "{}";
|
||||
toolCallsList.Add(new
|
||||
{
|
||||
id = b.GetProperty("id").GetString() ?? "",
|
||||
id = b.GetProperty("id").SafeGetString() ?? "",
|
||||
type = "function",
|
||||
function = new
|
||||
{
|
||||
name = b.GetProperty("name").GetString() ?? "",
|
||||
name = b.GetProperty("name").SafeGetString() ?? "",
|
||||
arguments = argsJson,
|
||||
}
|
||||
});
|
||||
@@ -817,6 +817,8 @@ public partial class LlmService
|
||||
["max_tokens"] = ResolveOpenAiCompatibleMaxTokens(),
|
||||
["parallel_tool_calls"] = executionPolicy.EnableParallelReadBatch,
|
||||
};
|
||||
// 스트리밍 시 마지막 청크에 토큰 사용량 포함 요청 (vLLM/OpenAI 호환)
|
||||
body["stream_options"] = new { include_usage = true };
|
||||
// tool_choice: "required" — 모델이 반드시 도구를 호출하도록 강제
|
||||
// 아직 한 번도 도구를 호출하지 않은 첫 번째 요청에서만 사용 (chatty 모델 대응)
|
||||
if (forceToolCall)
|
||||
@@ -877,8 +879,8 @@ public partial class LlmService
|
||||
msgs.Add(new
|
||||
{
|
||||
role = "tool",
|
||||
tool_call_id = root.GetProperty("tool_use_id").GetString(),
|
||||
content = root.GetProperty("content").GetString(),
|
||||
tool_call_id = root.GetProperty("tool_use_id").SafeGetString(),
|
||||
content = root.GetProperty("content").SafeGetString(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -896,19 +898,19 @@ public partial class LlmService
|
||||
var toolCallsList = new List<object>();
|
||||
foreach (var b in blocksArr.EnumerateArray())
|
||||
{
|
||||
var bType = b.GetProperty("type").GetString();
|
||||
var bType = b.GetProperty("type").SafeGetString();
|
||||
if (bType == "text")
|
||||
textContent = b.GetProperty("text").GetString() ?? "";
|
||||
textContent = b.GetProperty("text").SafeGetString() ?? "";
|
||||
else if (bType == "tool_use")
|
||||
{
|
||||
var argsJson = b.TryGetProperty("input", out var inp) ? inp.GetRawText() : "{}";
|
||||
var argsJson = b.SafeTryGetProperty("input", out var inp) ? inp.GetRawText() : "{}";
|
||||
toolCallsList.Add(new
|
||||
{
|
||||
id = b.GetProperty("id").GetString() ?? "",
|
||||
id = b.GetProperty("id").SafeGetString() ?? "",
|
||||
type = "function",
|
||||
function = new
|
||||
{
|
||||
name = b.GetProperty("name").GetString() ?? "",
|
||||
name = b.GetProperty("name").SafeGetString() ?? "",
|
||||
arguments = argsJson,
|
||||
}
|
||||
});
|
||||
@@ -1197,11 +1199,11 @@ public partial class LlmService
|
||||
TryParseOpenAiUsage(root);
|
||||
|
||||
if (usesIbmDeploymentApi &&
|
||||
root.TryGetProperty("status", out var statusEl) &&
|
||||
string.Equals(statusEl.GetString(), "error", StringComparison.OrdinalIgnoreCase))
|
||||
root.SafeTryGetProperty("status", out var statusEl) &&
|
||||
string.Equals(statusEl.SafeGetString(), "error", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var detail = root.TryGetProperty("message", out var msgEl)
|
||||
? msgEl.GetString()
|
||||
var detail = root.SafeTryGetProperty("message", out var msgEl)
|
||||
? msgEl.SafeGetString()
|
||||
: "IBM vLLM 도구 호출 응답 오류";
|
||||
throw new ToolCallNotSupportedException(detail ?? "IBM vLLM 도구 호출 응답 오류");
|
||||
}
|
||||
@@ -1223,15 +1225,15 @@ public partial class LlmService
|
||||
}
|
||||
|
||||
if (usesIbmDeploymentApi &&
|
||||
root.TryGetProperty("results", out var resultsEl) &&
|
||||
root.SafeTryGetProperty("results", out var resultsEl) &&
|
||||
resultsEl.ValueKind == JsonValueKind.Array &&
|
||||
resultsEl.GetArrayLength() > 0)
|
||||
{
|
||||
var first = resultsEl[0];
|
||||
var generatedText = first.TryGetProperty("generated_text", out var generatedTextEl)
|
||||
? generatedTextEl.GetString()
|
||||
: first.TryGetProperty("output_text", out var outputTextEl)
|
||||
? outputTextEl.GetString()
|
||||
var generatedText = first.SafeTryGetProperty("generated_text", out var generatedTextEl)
|
||||
? generatedTextEl.SafeGetString()
|
||||
: first.SafeTryGetProperty("output_text", out var outputTextEl)
|
||||
? outputTextEl.SafeGetString()
|
||||
: null;
|
||||
if (!string.IsNullOrEmpty(generatedText))
|
||||
{
|
||||
@@ -1251,18 +1253,18 @@ public partial class LlmService
|
||||
}
|
||||
}
|
||||
|
||||
if (root.TryGetProperty("choices", out var choicesEl) &&
|
||||
if (root.SafeTryGetProperty("choices", out var choicesEl) &&
|
||||
choicesEl.ValueKind == JsonValueKind.Array &&
|
||||
choicesEl.GetArrayLength() > 0)
|
||||
{
|
||||
var firstChoice = choicesEl[0];
|
||||
if (firstChoice.TryGetProperty("delta", out var deltaEl))
|
||||
if (firstChoice.SafeTryGetProperty("delta", out var deltaEl))
|
||||
{
|
||||
var emittedContent = false;
|
||||
if (deltaEl.TryGetProperty("content", out var contentEl) &&
|
||||
if (deltaEl.SafeTryGetProperty("content", out var contentEl) &&
|
||||
contentEl.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var chunk = contentEl.GetString();
|
||||
var chunk = contentEl.SafeGetString();
|
||||
if (!string.IsNullOrEmpty(chunk))
|
||||
{
|
||||
yield return new ToolStreamEvent(ToolStreamEventKind.TextDelta, chunk);
|
||||
@@ -1272,20 +1274,20 @@ public partial class LlmService
|
||||
// Qwen3.5 thinking 모드 폴백: content가 비어있거나 없으면 reasoning_content 사용
|
||||
// else if가 아닌 독립 if — content 키가 존재하되 빈 문자열("")인 경우도 커버
|
||||
if (!emittedContent &&
|
||||
deltaEl.TryGetProperty("reasoning_content", out var reasoningEl) &&
|
||||
deltaEl.SafeTryGetProperty("reasoning_content", out var reasoningEl) &&
|
||||
reasoningEl.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var reasoningChunk = reasoningEl.GetString();
|
||||
var reasoningChunk = reasoningEl.SafeGetString();
|
||||
if (!string.IsNullOrEmpty(reasoningChunk))
|
||||
yield return new ToolStreamEvent(ToolStreamEventKind.TextDelta, reasoningChunk);
|
||||
}
|
||||
|
||||
if (deltaEl.TryGetProperty("tool_calls", out var toolCallsEl) &&
|
||||
if (deltaEl.SafeTryGetProperty("tool_calls", out var toolCallsEl) &&
|
||||
toolCallsEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var toolCallEl in toolCallsEl.EnumerateArray())
|
||||
{
|
||||
var index = toolCallEl.TryGetProperty("index", out var indexEl) &&
|
||||
var index = toolCallEl.SafeTryGetProperty("index", out var indexEl) &&
|
||||
indexEl.TryGetInt32(out var parsedIndex)
|
||||
? parsedIndex
|
||||
: toolAccumulators.Count;
|
||||
@@ -1296,18 +1298,18 @@ public partial class LlmService
|
||||
toolAccumulators[index] = acc;
|
||||
}
|
||||
|
||||
if (toolCallEl.TryGetProperty("id", out var idEl) && idEl.ValueKind == JsonValueKind.String)
|
||||
acc.Id = idEl.GetString() ?? acc.Id;
|
||||
if (toolCallEl.SafeTryGetProperty("id", out var idEl) && idEl.ValueKind == JsonValueKind.String)
|
||||
acc.Id = idEl.SafeGetString() ?? acc.Id;
|
||||
|
||||
if (toolCallEl.TryGetProperty("function", out var functionEl))
|
||||
if (toolCallEl.SafeTryGetProperty("function", out var functionEl))
|
||||
{
|
||||
if (functionEl.TryGetProperty("name", out var nameEl) && nameEl.ValueKind == JsonValueKind.String)
|
||||
acc.Name = nameEl.GetString() ?? acc.Name;
|
||||
if (functionEl.SafeTryGetProperty("name", out var nameEl) && nameEl.ValueKind == JsonValueKind.String)
|
||||
acc.Name = nameEl.SafeGetString() ?? acc.Name;
|
||||
|
||||
if (functionEl.TryGetProperty("arguments", out var argumentsEl))
|
||||
if (functionEl.SafeTryGetProperty("arguments", out var argumentsEl))
|
||||
{
|
||||
if (argumentsEl.ValueKind == JsonValueKind.String)
|
||||
acc.Arguments.Append(argumentsEl.GetString());
|
||||
acc.Arguments.Append(argumentsEl.SafeGetString());
|
||||
else if (argumentsEl.ValueKind is JsonValueKind.Object or JsonValueKind.Array)
|
||||
acc.Arguments.Append(argumentsEl.GetRawText());
|
||||
}
|
||||
@@ -1320,7 +1322,7 @@ public partial class LlmService
|
||||
}
|
||||
}
|
||||
|
||||
if (firstChoice.TryGetProperty("message", out var messageEl))
|
||||
if (firstChoice.SafeTryGetProperty("message", out var messageEl))
|
||||
{
|
||||
if (TryExtractMessageToolBlocks(messageEl, out var messageText2, out var directToolBlocks2))
|
||||
{
|
||||
@@ -1358,14 +1360,14 @@ public partial class LlmService
|
||||
text = "";
|
||||
toolBlocks = new List<ContentBlock>();
|
||||
JsonElement message = messageOrRoot;
|
||||
if (messageOrRoot.TryGetProperty("message", out var nestedMessage))
|
||||
if (messageOrRoot.SafeTryGetProperty("message", out var nestedMessage))
|
||||
message = nestedMessage;
|
||||
|
||||
var consumed = false;
|
||||
if (message.TryGetProperty("content", out var contentEl) &&
|
||||
if (message.SafeTryGetProperty("content", out var contentEl) &&
|
||||
contentEl.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var parsedText = contentEl.GetString();
|
||||
var parsedText = contentEl.SafeGetString();
|
||||
if (!string.IsNullOrWhiteSpace(parsedText))
|
||||
{
|
||||
text = parsedText;
|
||||
@@ -1374,10 +1376,10 @@ public partial class LlmService
|
||||
}
|
||||
// Qwen3.5 thinking 모드 폴백: content가 비어있으면 reasoning_content 사용
|
||||
if (!consumed &&
|
||||
message.TryGetProperty("reasoning_content", out var reasoningContentEl) &&
|
||||
message.SafeTryGetProperty("reasoning_content", out var reasoningContentEl) &&
|
||||
reasoningContentEl.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var reasoningText = reasoningContentEl.GetString();
|
||||
var reasoningText = reasoningContentEl.SafeGetString();
|
||||
if (!string.IsNullOrWhiteSpace(reasoningText))
|
||||
{
|
||||
text = reasoningText;
|
||||
@@ -1385,22 +1387,22 @@ public partial class LlmService
|
||||
}
|
||||
}
|
||||
|
||||
if (message.TryGetProperty("tool_calls", out var toolCallsEl) &&
|
||||
if (message.SafeTryGetProperty("tool_calls", out var toolCallsEl) &&
|
||||
toolCallsEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var tc in toolCallsEl.EnumerateArray())
|
||||
{
|
||||
if (!tc.TryGetProperty("function", out var functionEl))
|
||||
if (!tc.SafeTryGetProperty("function", out var functionEl))
|
||||
continue;
|
||||
|
||||
JsonElement? parsedArgs = null;
|
||||
if (functionEl.TryGetProperty("arguments", out var argsEl))
|
||||
if (functionEl.SafeTryGetProperty("arguments", out var argsEl))
|
||||
{
|
||||
if (argsEl.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var argsDoc = JsonDocument.Parse(argsEl.GetString() ?? "{}");
|
||||
using var argsDoc = JsonDocument.Parse(argsEl.SafeGetString() ?? "{}");
|
||||
parsedArgs = argsDoc.RootElement.Clone();
|
||||
}
|
||||
catch { parsedArgs = null; }
|
||||
@@ -1414,8 +1416,8 @@ public partial class LlmService
|
||||
toolBlocks.Add(new ContentBlock
|
||||
{
|
||||
Type = "tool_use",
|
||||
ToolName = functionEl.TryGetProperty("name", out var nameEl) ? nameEl.GetString() ?? "" : "",
|
||||
ToolId = tc.TryGetProperty("id", out var idEl) ? idEl.GetString() ?? Guid.NewGuid().ToString("N")[..12] : Guid.NewGuid().ToString("N")[..12],
|
||||
ToolName = functionEl.SafeTryGetProperty("name", out var nameEl) ? nameEl.SafeGetString() ?? "" : "",
|
||||
ToolId = tc.SafeTryGetProperty("id", out var idEl) ? idEl.SafeGetString() ?? Guid.NewGuid().ToString("N")[..12] : Guid.NewGuid().ToString("N")[..12],
|
||||
ToolInput = parsedArgs,
|
||||
});
|
||||
}
|
||||
@@ -1542,12 +1544,12 @@ public partial class LlmService
|
||||
{
|
||||
using var doc = JsonDocument.Parse(errBody);
|
||||
// Ollama: {"error":"..."}
|
||||
if (doc.RootElement.TryGetProperty("error", out var err))
|
||||
if (doc.RootElement.SafeTryGetProperty("error", out var err))
|
||||
{
|
||||
if (err.ValueKind == JsonValueKind.String)
|
||||
return err.GetString() ?? errBody;
|
||||
if (err.ValueKind == JsonValueKind.Object && err.TryGetProperty("message", out var m))
|
||||
return m.GetString() ?? errBody;
|
||||
return err.SafeGetString() ?? errBody;
|
||||
if (err.ValueKind == JsonValueKind.Object && err.SafeTryGetProperty("message", out var m))
|
||||
return m.SafeGetString() ?? errBody;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Reference in New Issue
Block a user