using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; using AxCopilot.Models; using AxCopilot.Services.Agent; namespace AxCopilot.Services; public class LlmService : IDisposable { public class ContentBlock { public string Type { get; init; } = "text"; public string Text { get; init; } = ""; public string ToolName { get; init; } = ""; public string ToolId { get; init; } = ""; public JsonElement? ToolInput { get; init; } } [CompilerGenerated] private sealed class _003CStreamAsync_003Ed__34 : IAsyncEnumerable, IAsyncEnumerator, IAsyncDisposable, IValueTaskSource, IValueTaskSource, IAsyncStateMachine { public int _003C_003E1__state; public AsyncIteratorMethodBuilder _003C_003Et__builder; public ManualResetValueTaskSourceCore _003C_003Ev__promiseOfValueOrEnd; private string _003C_003E2__current; private bool _003C_003Ew__disposeMode; private CancellationTokenSource _003C_003Ex__combinedTokens; private int _003C_003El__initialThreadId; private List messages; public List _003C_003E3__messages; private CancellationToken ct; public CancellationToken _003C_003E3__ct; public LlmService _003C_003E4__this; private string _003CactiveService_003E5__1; private IAsyncEnumerable _003Cstream_003E5__2; private string _003C_003Es__3; private ConfiguredCancelableAsyncEnumerable.Enumerator _003C_003Es__4; private object _003C_003Es__5; private int _003C_003Es__6; private string _003Cchunk_003E5__7; private bool _003C_003Es__8; private ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter _003C_003Eu__1; private ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter _003C_003Eu__2; string IAsyncEnumerator.Current { [DebuggerHidden] get { return _003C_003E2__current; } } [DebuggerHidden] public _003CStreamAsync_003Ed__34(int _003C_003E1__state) { _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); this._003C_003E1__state = _003C_003E1__state; _003C_003El__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = _003C_003E1__state; try { ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter; switch (num) { default: if (!_003C_003Ew__disposeMode) { num = (_003C_003E1__state = -1); _003CactiveService_003E5__1 = _003C_003E4__this.ResolveService(); _003C_003Es__3 = _003CactiveService_003E5__1.ToLowerInvariant(); if (1 == 0) { } IAsyncEnumerable asyncEnumerable = _003C_003Es__3 switch { "gemini" => _003C_003E4__this.StreamGeminiAsync(messages, ct), "claude" => _003C_003E4__this.StreamClaudeAsync(messages, ct), "vllm" => _003C_003E4__this.StreamOpenAiCompatibleAsync(messages, ct), _ => _003C_003E4__this.StreamOllamaAsync(messages, ct), }; if (1 == 0) { } _003Cstream_003E5__2 = asyncEnumerable; _003C_003Es__3 = null; _003C_003Es__4 = _003Cstream_003E5__2.WithCancellation(ct).GetAsyncEnumerator(); _003C_003Es__5 = null; _003C_003Es__6 = 0; goto case -4; } goto end_IL_0007; case -4: case 0: try { ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter2; if (num != -4) { if (num != 0) { goto IL_01aa; } awaiter2 = _003C_003Eu__1; _003C_003Eu__1 = default(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter); num = (_003C_003E1__state = -1); goto IL_0216; } num = (_003C_003E1__state = -1); if (!_003C_003Ew__disposeMode) { _003Cchunk_003E5__7 = null; goto IL_01aa; } goto end_IL_014c; IL_01aa: _003C_003E2__current = null; awaiter2 = _003C_003Es__4.MoveNextAsync().GetAwaiter(); if (!awaiter2.IsCompleted) { num = (_003C_003E1__state = 0); _003C_003Eu__1 = awaiter2; _003CStreamAsync_003Ed__34 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } goto IL_0216; IL_0216: _003C_003Es__8 = awaiter2.GetResult(); if (_003C_003Es__8) { _003Cchunk_003E5__7 = _003C_003Es__4.Current; _003C_003E2__current = _003Cchunk_003E5__7; num = (_003C_003E1__state = -4); goto IL_03ef; } end_IL_014c:; } catch (object obj) { _003C_003Es__5 = obj; } _003C_003E2__current = null; awaiter = _003C_003Es__4.DisposeAsync().GetAwaiter(); if (!awaiter.IsCompleted) { num = (_003C_003E1__state = 1); _003C_003Eu__2 = awaiter; _003CStreamAsync_003Ed__34 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; case 1: awaiter = _003C_003Eu__2; _003C_003Eu__2 = default(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter); num = (_003C_003E1__state = -1); break; } awaiter.GetResult(); object obj2 = _003C_003Es__5; if (obj2 != null) { if (!(obj2 is Exception source)) { throw obj2; } ExceptionDispatchInfo.Capture(source).Throw(); } _ = _003C_003Es__6; if (!_003C_003Ew__disposeMode) { _003C_003Es__5 = null; _003C_003Es__4 = default(ConfiguredCancelableAsyncEnumerable.Enumerator); } end_IL_0007:; } catch (Exception exception) { _003C_003E1__state = -2; _003CactiveService_003E5__1 = null; _003Cstream_003E5__2 = null; _003C_003Es__3 = null; _003C_003Es__4 = default(ConfiguredCancelableAsyncEnumerable.Enumerator); _003C_003Es__5 = null; _003Cchunk_003E5__7 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetException(exception); return; } _003C_003E1__state = -2; _003CactiveService_003E5__1 = null; _003Cstream_003E5__2 = null; _003C_003Es__3 = null; _003C_003Es__4 = default(ConfiguredCancelableAsyncEnumerable.Enumerator); _003C_003Es__5 = null; _003Cchunk_003E5__7 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetResult(result: false); return; IL_03ef: _003C_003Ev__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { _003CStreamAsync_003Ed__34 _003CStreamAsync_003Ed__; if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId) { _003C_003E1__state = -3; _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); _003C_003Ew__disposeMode = false; _003CStreamAsync_003Ed__ = this; } else { _003CStreamAsync_003Ed__ = new _003CStreamAsync_003Ed__34(-3) { _003C_003E4__this = _003C_003E4__this }; } _003CStreamAsync_003Ed__.messages = _003C_003E3__messages; if (_003C_003E3__ct.Equals(default(CancellationToken))) { _003CStreamAsync_003Ed__.ct = cancellationToken; } else if (cancellationToken.Equals(_003C_003E3__ct) || cancellationToken.Equals(default(CancellationToken))) { _003CStreamAsync_003Ed__.ct = _003C_003E3__ct; } else { _003C_003Ex__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(_003C_003E3__ct, cancellationToken); _003CStreamAsync_003Ed__.ct = _003C_003Ex__combinedTokens.Token; } return _003CStreamAsync_003Ed__; } [DebuggerHidden] ValueTask IAsyncEnumerator.MoveNextAsync() { if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamAsync_003Ed__34 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); short version = _003C_003Ev__promiseOfValueOrEnd.Version; if (_003C_003Ev__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask(_003C_003Ev__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask(this, version); } [DebuggerHidden] bool IValueTaskSource.GetResult(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (_003C_003E1__state >= -1) { throw new NotSupportedException(); } if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ew__disposeMode = true; _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamAsync_003Ed__34 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); return new ValueTask(this, _003C_003Ev__promiseOfValueOrEnd.Version); } } [CompilerGenerated] private sealed class _003CStreamClaudeAsync_003Ed__46 : IAsyncEnumerable, IAsyncEnumerator, IAsyncDisposable, IValueTaskSource, IValueTaskSource, IAsyncStateMachine { public int _003C_003E1__state; public AsyncIteratorMethodBuilder _003C_003Et__builder; public ManualResetValueTaskSourceCore _003C_003Ev__promiseOfValueOrEnd; private string _003C_003E2__current; private bool _003C_003Ew__disposeMode; private CancellationTokenSource _003C_003Ex__combinedTokens; private int _003C_003El__initialThreadId; private List messages; public List _003C_003E3__messages; private CancellationToken ct; public CancellationToken _003C_003E3__ct; public LlmService _003C_003E4__this; private LlmSettings _003Cllm_003E5__1; private string _003CapiKey_003E5__2; private object _003Cbody_003E5__3; private string _003Cjson_003E5__4; private HttpRequestMessage _003Creq_003E5__5; private HttpResponseMessage _003Cresp_003E5__6; private Stream _003Cstream_003E5__7; private StreamReader _003Creader_003E5__8; private HttpResponseMessage _003C_003Es__9; private string _003CerrBody_003E5__10; private string _003C_003Es__11; private Stream _003C_003Es__12; private string _003Cline_003E5__13; private string _003Cdata_003E5__14; private string _003Ctext_003E5__15; private string _003C_003Es__16; private JsonDocument _003Cdoc_003E5__17; private string _003Ctype_003E5__18; private JsonElement _003Cdelta_003E5__19; private JsonElement _003Ct_003E5__20; private JsonElement _003Cmsg_003E5__21; private JsonElement _003Cu1_003E5__22; private JsonElement _003Cu2_003E5__23; private JsonException _003Cex_003E5__24; private TaskAwaiter _003C_003Eu__1; private TaskAwaiter _003C_003Eu__2; private TaskAwaiter _003C_003Eu__3; string IAsyncEnumerator.Current { [DebuggerHidden] get { return _003C_003E2__current; } } [DebuggerHidden] public _003CStreamClaudeAsync_003Ed__46(int _003C_003E1__state) { _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); this._003C_003E1__state = _003C_003E1__state; _003C_003El__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = _003C_003E1__state; try { switch (num) { default: if (!_003C_003Ew__disposeMode) { num = (_003C_003E1__state = -1); _003Cllm_003E5__1 = _003C_003E4__this._settings.Settings.Llm; _003CapiKey_003E5__2 = _003Cllm_003E5__1.ApiKey; if (string.IsNullOrEmpty(_003CapiKey_003E5__2)) { throw new InvalidOperationException("Claude API 키가 설정되지 않았습니다."); } _003Cbody_003E5__3 = _003C_003E4__this.BuildClaudeBody(messages, stream: true); _003Cjson_003E5__4 = JsonSerializer.Serialize(_003Cbody_003E5__3); _003Creq_003E5__5 = new HttpRequestMessage(HttpMethod.Post, "https://api.anthropic.com/v1/messages"); break; } goto end_IL_0007; case -4: case 0: case 1: case 2: case 3: break; } try { TaskAwaiter awaiter; switch (num) { default: _003Creq_003E5__5.Content = new StringContent(_003Cjson_003E5__4, Encoding.UTF8, "application/json"); _003Creq_003E5__5.Headers.Add("x-api-key", _003CapiKey_003E5__2); _003Creq_003E5__5.Headers.Add("anthropic-version", "2023-06-01"); _003C_003E2__current = null; awaiter = _003C_003E4__this._http.SendAsync(_003Creq_003E5__5, HttpCompletionOption.ResponseHeadersRead, ct).GetAwaiter(); if (!awaiter.IsCompleted) { num = (_003C_003E1__state = 0); _003C_003Eu__1 = awaiter; _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } goto IL_01d9; case 0: awaiter = _003C_003Eu__1; _003C_003Eu__1 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_01d9; case -4: case 1: case 2: case 3: break; IL_01d9: _003C_003Es__9 = awaiter.GetResult(); _003Cresp_003E5__6 = _003C_003Es__9; _003C_003Es__9 = null; break; } try { TaskAwaiter awaiter3; TaskAwaiter awaiter2; switch (num) { default: if (!_003Cresp_003E5__6.IsSuccessStatusCode) { _003C_003E2__current = null; awaiter3 = _003Cresp_003E5__6.Content.ReadAsStringAsync(ct).GetAwaiter(); if (!awaiter3.IsCompleted) { num = (_003C_003E1__state = 1); _003C_003Eu__2 = awaiter3; _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } goto IL_02ad; } _003C_003E2__current = null; awaiter2 = _003Cresp_003E5__6.Content.ReadAsStreamAsync(ct).GetAwaiter(); if (!awaiter2.IsCompleted) { num = (_003C_003E1__state = 2); _003C_003Eu__3 = awaiter2; _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } goto IL_0356; case 1: awaiter3 = _003C_003Eu__2; _003C_003Eu__2 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_02ad; case 2: awaiter2 = _003C_003Eu__3; _003C_003Eu__3 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_0356; case -4: case 3: break; IL_02ad: _003C_003Es__11 = awaiter3.GetResult(); _003CerrBody_003E5__10 = _003C_003Es__11; _003C_003Es__11 = null; throw new HttpRequestException(ClassifyHttpError(_003Cresp_003E5__6, _003CerrBody_003E5__10)); IL_0356: _003C_003Es__12 = awaiter2.GetResult(); _003Cstream_003E5__7 = _003C_003Es__12; _003C_003Es__12 = null; break; } try { if (num != -4 && num != 3) { _003Creader_003E5__8 = new StreamReader(_003Cstream_003E5__7); } try { TaskAwaiter awaiter4; if (num != -4) { if (num != 3) { goto IL_071e; } awaiter4 = _003C_003Eu__2; _003C_003Eu__2 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_041f; } num = (_003C_003E1__state = -1); if (!_003C_003Ew__disposeMode) { goto IL_0708; } goto end_IL_0398; IL_0708: _003Cline_003E5__13 = null; _003Cdata_003E5__14 = null; _003Ctext_003E5__15 = null; goto IL_071e; IL_06c6: if (string.IsNullOrEmpty(_003Ctext_003E5__15)) { goto IL_0708; } _003C_003E2__current = _003Ctext_003E5__15; num = (_003C_003E1__state = -4); goto IL_0a2d; IL_041f: _003C_003Es__16 = awaiter4.GetResult(); _003Cline_003E5__13 = _003C_003Es__16; _003C_003Es__16 = null; if (_003Cline_003E5__13 != null) { if (string.IsNullOrEmpty(_003Cline_003E5__13) || !_003Cline_003E5__13.StartsWith("data: ")) { goto IL_071e; } string text = _003Cline_003E5__13; int length = "data: ".Length; _003Cdata_003E5__14 = text.Substring(length, text.Length - length); _003Ctext_003E5__15 = null; try { _003Cdoc_003E5__17 = JsonDocument.Parse(_003Cdata_003E5__14); try { _003Ctype_003E5__18 = _003Cdoc_003E5__17.RootElement.GetProperty("type").GetString(); if (_003Ctype_003E5__18 == "content_block_delta") { _003Cdelta_003E5__19 = _003Cdoc_003E5__17.RootElement.GetProperty("delta"); if (_003Cdelta_003E5__19.TryGetProperty("text", out _003Ct_003E5__20)) { _003Ctext_003E5__15 = _003Ct_003E5__20.GetString(); } _003Cdelta_003E5__19 = default(JsonElement); _003Ct_003E5__20 = default(JsonElement); } else { text = _003Ctype_003E5__18; if ((text == "message_start" || text == "message_delta") ? true : false) { if (_003Cdoc_003E5__17.RootElement.TryGetProperty("message", out _003Cmsg_003E5__21) && _003Cmsg_003E5__21.TryGetProperty("usage", out _003Cu1_003E5__22)) { _003C_003E4__this.TryParseClaudeUsageFromElement(_003Cu1_003E5__22); } else { if (_003Cdoc_003E5__17.RootElement.TryGetProperty("usage", out _003Cu2_003E5__23)) { _003C_003E4__this.TryParseClaudeUsageFromElement(_003Cu2_003E5__23); } _003Cu2_003E5__23 = default(JsonElement); } _003Cmsg_003E5__21 = default(JsonElement); _003Cu1_003E5__22 = default(JsonElement); } } } finally { if (num == -1 && _003Cdoc_003E5__17 != null) { ((IDisposable)_003Cdoc_003E5__17).Dispose(); } } if (!_003C_003Ew__disposeMode) { _003Cdoc_003E5__17 = null; _003Ctype_003E5__18 = null; goto IL_06c6; } } catch (JsonException ex) { _003Cex_003E5__24 = ex; LogService.Warn("Claude 스트리밍 JSON 파싱 오류: " + _003Cex_003E5__24.Message); goto IL_06c6; } goto end_IL_0398; } goto end_IL_0377; IL_071e: if (!_003Creader_003E5__8.EndOfStream && !ct.IsCancellationRequested) { _003C_003E2__current = null; awaiter4 = ReadLineWithTimeoutAsync(_003Creader_003E5__8, ct).GetAwaiter(); if (!awaiter4.IsCompleted) { num = (_003C_003E1__state = 3); _003C_003Eu__2 = awaiter4; _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter4, ref stateMachine); return; } goto IL_041f; } goto end_IL_0377; end_IL_0398:; } finally { if (num == -1 && _003Creader_003E5__8 != null) { ((IDisposable)_003Creader_003E5__8).Dispose(); } } if (!_003C_003Ew__disposeMode) { } goto IL_0788; end_IL_0377:; } finally { if (num == -1 && _003Cstream_003E5__7 != null) { ((IDisposable)_003Cstream_003E5__7).Dispose(); } } goto end_IL_01fa; IL_0788: if (!_003C_003Ew__disposeMode) { } goto IL_07ad; end_IL_01fa:; } finally { if (num == -1 && _003Cresp_003E5__6 != null) { ((IDisposable)_003Cresp_003E5__6).Dispose(); } } goto end_IL_00d6; IL_07ad: if (!_003C_003Ew__disposeMode) { } goto IL_07d2; end_IL_00d6:; } finally { if (num == -1 && _003Creq_003E5__5 != null) { ((IDisposable)_003Creq_003E5__5).Dispose(); } } goto end_IL_0007; IL_07d2: if (!_003C_003Ew__disposeMode) { _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cbody_003E5__3 = null; _003Cjson_003E5__4 = null; _003Creq_003E5__5 = null; _003Cresp_003E5__6 = null; _003Cstream_003E5__7 = null; _003Creader_003E5__8 = null; } end_IL_0007:; } catch (Exception exception) { _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cbody_003E5__3 = null; _003Cjson_003E5__4 = null; _003Creq_003E5__5 = null; _003Cresp_003E5__6 = null; _003Cstream_003E5__7 = null; _003Creader_003E5__8 = null; _003C_003Es__9 = null; _003CerrBody_003E5__10 = null; _003C_003Es__11 = null; _003C_003Es__12 = null; _003Cline_003E5__13 = null; _003Cdata_003E5__14 = null; _003Ctext_003E5__15 = null; _003C_003Es__16 = null; _003Cdoc_003E5__17 = null; _003Ctype_003E5__18 = null; _003Cdelta_003E5__19 = default(JsonElement); _003Ct_003E5__20 = default(JsonElement); _003Cmsg_003E5__21 = default(JsonElement); _003Cu1_003E5__22 = default(JsonElement); _003Cu2_003E5__23 = default(JsonElement); _003Cex_003E5__24 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetException(exception); return; } _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cbody_003E5__3 = null; _003Cjson_003E5__4 = null; _003Creq_003E5__5 = null; _003Cresp_003E5__6 = null; _003Cstream_003E5__7 = null; _003Creader_003E5__8 = null; _003C_003Es__9 = null; _003CerrBody_003E5__10 = null; _003C_003Es__11 = null; _003C_003Es__12 = null; _003Cline_003E5__13 = null; _003Cdata_003E5__14 = null; _003Ctext_003E5__15 = null; _003C_003Es__16 = null; _003Cdoc_003E5__17 = null; _003Ctype_003E5__18 = null; _003Cdelta_003E5__19 = default(JsonElement); _003Ct_003E5__20 = default(JsonElement); _003Cmsg_003E5__21 = default(JsonElement); _003Cu1_003E5__22 = default(JsonElement); _003Cu2_003E5__23 = default(JsonElement); _003Cex_003E5__24 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetResult(result: false); return; IL_0a2d: _003C_003Ev__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { _003CStreamClaudeAsync_003Ed__46 _003CStreamClaudeAsync_003Ed__; if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId) { _003C_003E1__state = -3; _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); _003C_003Ew__disposeMode = false; _003CStreamClaudeAsync_003Ed__ = this; } else { _003CStreamClaudeAsync_003Ed__ = new _003CStreamClaudeAsync_003Ed__46(-3) { _003C_003E4__this = _003C_003E4__this }; } _003CStreamClaudeAsync_003Ed__.messages = _003C_003E3__messages; if (_003C_003E3__ct.Equals(default(CancellationToken))) { _003CStreamClaudeAsync_003Ed__.ct = cancellationToken; } else if (cancellationToken.Equals(_003C_003E3__ct) || cancellationToken.Equals(default(CancellationToken))) { _003CStreamClaudeAsync_003Ed__.ct = _003C_003E3__ct; } else { _003C_003Ex__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(_003C_003E3__ct, cancellationToken); _003CStreamClaudeAsync_003Ed__.ct = _003C_003Ex__combinedTokens.Token; } return _003CStreamClaudeAsync_003Ed__; } [DebuggerHidden] ValueTask IAsyncEnumerator.MoveNextAsync() { if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); short version = _003C_003Ev__promiseOfValueOrEnd.Version; if (_003C_003Ev__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask(_003C_003Ev__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask(this, version); } [DebuggerHidden] bool IValueTaskSource.GetResult(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (_003C_003E1__state >= -1) { throw new NotSupportedException(); } if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ew__disposeMode = true; _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamClaudeAsync_003Ed__46 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); return new ValueTask(this, _003C_003Ev__promiseOfValueOrEnd.Version); } } [CompilerGenerated] private sealed class _003CStreamGeminiAsync_003Ed__43 : IAsyncEnumerable, IAsyncEnumerator, IAsyncDisposable, IValueTaskSource, IValueTaskSource, IAsyncStateMachine { public int _003C_003E1__state; public AsyncIteratorMethodBuilder _003C_003Et__builder; public ManualResetValueTaskSourceCore _003C_003Ev__promiseOfValueOrEnd; private string _003C_003E2__current; private bool _003C_003Ew__disposeMode; private CancellationTokenSource _003C_003Ex__combinedTokens; private int _003C_003El__initialThreadId; private List messages; public List _003C_003E3__messages; private CancellationToken ct; public CancellationToken _003C_003E3__ct; public LlmService _003C_003E4__this; private LlmSettings _003Cllm_003E5__1; private string _003CapiKey_003E5__2; private string _003Cmodel_003E5__3; private object _003Cbody_003E5__4; private string _003Curl_003E5__5; private HttpRequestMessage _003Creq_003E5__6; private HttpResponseMessage _003Cresp_003E5__7; private Stream _003Cstream_003E5__8; private StreamReader _003Creader_003E5__9; private HttpResponseMessage _003C_003Es__10; private Stream _003C_003Es__11; private string _003Cline_003E5__12; private string _003Cdata_003E5__13; private string _003Cparsed_003E5__14; private string _003C_003Es__15; private JsonDocument _003Cdoc_003E5__16; private JsonElement _003Ccandidates_003E5__17; private StringBuilder _003Csb_003E5__18; private JsonElement _003Cparts_003E5__19; private JsonElement.ArrayEnumerator _003C_003Es__20; private JsonElement _003Cpart_003E5__21; private JsonElement _003Ct_003E5__22; private string _003Ctext_003E5__23; private JsonException _003Cex_003E5__24; private TaskAwaiter _003C_003Eu__1; private TaskAwaiter _003C_003Eu__2; private TaskAwaiter _003C_003Eu__3; string IAsyncEnumerator.Current { [DebuggerHidden] get { return _003C_003E2__current; } } [DebuggerHidden] public _003CStreamGeminiAsync_003Ed__43(int _003C_003E1__state) { _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); this._003C_003E1__state = _003C_003E1__state; _003C_003El__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = _003C_003E1__state; try { switch (num) { default: if (!_003C_003Ew__disposeMode) { num = (_003C_003E1__state = -1); _003Cllm_003E5__1 = _003C_003E4__this._settings.Settings.Llm; _003CapiKey_003E5__2 = _003C_003E4__this.ResolveApiKeyForService("gemini"); if (string.IsNullOrEmpty(_003CapiKey_003E5__2)) { throw new InvalidOperationException("Gemini API 키가 설정되지 않았습니다."); } _003Cmodel_003E5__3 = _003C_003E4__this.ResolveModel(); _003Cbody_003E5__4 = _003C_003E4__this.BuildGeminiBody(messages); _003Curl_003E5__5 = "https://generativelanguage.googleapis.com/v1beta/models/" + _003Cmodel_003E5__3 + ":streamGenerateContent?alt=sse&key=" + _003CapiKey_003E5__2; _003Creq_003E5__6 = new HttpRequestMessage(HttpMethod.Post, _003Curl_003E5__5) { Content = JsonContent(_003Cbody_003E5__4) }; break; } goto end_IL_0007; case -4: case 0: case 1: case 2: break; } try { TaskAwaiter awaiter; switch (num) { default: _003C_003E2__current = null; awaiter = _003C_003E4__this.SendWithErrorClassificationAsync(_003Creq_003E5__6, ct).GetAwaiter(); if (!awaiter.IsCompleted) { num = (_003C_003E1__state = 0); _003C_003Eu__1 = awaiter; _003CStreamGeminiAsync_003Ed__43 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } goto IL_01a7; case 0: awaiter = _003C_003Eu__1; _003C_003Eu__1 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_01a7; case -4: case 1: case 2: break; IL_01a7: _003C_003Es__10 = awaiter.GetResult(); _003Cresp_003E5__7 = _003C_003Es__10; _003C_003Es__10 = null; break; } try { if (num != -4) { TaskAwaiter awaiter2; if (num != 1) { if (num == 2) { goto IL_0275; } _003C_003E2__current = null; awaiter2 = _003Cresp_003E5__7.Content.ReadAsStreamAsync(ct).GetAwaiter(); if (!awaiter2.IsCompleted) { num = (_003C_003E1__state = 1); _003C_003Eu__2 = awaiter2; _003CStreamGeminiAsync_003Ed__43 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } } else { awaiter2 = _003C_003Eu__2; _003C_003Eu__2 = default(TaskAwaiter); num = (_003C_003E1__state = -1); } _003C_003Es__11 = awaiter2.GetResult(); _003Cstream_003E5__8 = _003C_003Es__11; _003C_003Es__11 = null; } goto IL_0275; IL_0699: if (!_003C_003Ew__disposeMode) { } goto IL_06be; IL_0275: try { if (num != -4 && num != 2) { _003Creader_003E5__9 = new StreamReader(_003Cstream_003E5__8); } try { TaskAwaiter awaiter3; if (num != -4) { if (num != 2) { goto IL_062f; } awaiter3 = _003C_003Eu__3; _003C_003Eu__3 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_031d; } num = (_003C_003E1__state = -1); if (!_003C_003Ew__disposeMode) { goto IL_0619; } goto end_IL_0296; IL_0619: _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Cparsed_003E5__14 = null; goto IL_062f; IL_05dc: if (_003Cparsed_003E5__14 == null) { goto IL_0619; } _003C_003E2__current = _003Cparsed_003E5__14; num = (_003C_003E1__state = -4); goto IL_0945; IL_031d: _003C_003Es__15 = awaiter3.GetResult(); _003Cline_003E5__12 = _003C_003Es__15; _003C_003Es__15 = null; if (_003Cline_003E5__12 != null) { if (!string.IsNullOrEmpty(_003Cline_003E5__12) && _003Cline_003E5__12.StartsWith("data: ")) { string text = _003Cline_003E5__12; int length = "data: ".Length; _003Cdata_003E5__13 = text.Substring(length, text.Length - length); _003Cparsed_003E5__14 = null; try { _003Cdoc_003E5__16 = JsonDocument.Parse(_003Cdata_003E5__13); try { _003C_003E4__this.TryParseGeminiUsage(_003Cdoc_003E5__16.RootElement); _003Ccandidates_003E5__17 = _003Cdoc_003E5__16.RootElement.GetProperty("candidates"); if (_003Ccandidates_003E5__17.GetArrayLength() != 0) { _003Csb_003E5__18 = new StringBuilder(); _003Cparts_003E5__19 = _003Ccandidates_003E5__17[0].GetProperty("content").GetProperty("parts"); _003C_003Es__20 = _003Cparts_003E5__19.EnumerateArray().GetEnumerator(); try { while (_003C_003Es__20.MoveNext()) { _003Cpart_003E5__21 = _003C_003Es__20.Current; if (_003Cpart_003E5__21.TryGetProperty("text", out _003Ct_003E5__22)) { _003Ctext_003E5__23 = _003Ct_003E5__22.GetString(); if (!string.IsNullOrEmpty(_003Ctext_003E5__23)) { _003Csb_003E5__18.Append(_003Ctext_003E5__23); } _003Ctext_003E5__23 = null; } _003Ct_003E5__22 = default(JsonElement); _003Cpart_003E5__21 = default(JsonElement); } } finally { if (num == -1) { ((IDisposable)_003C_003Es__20/*cast due to .constrained prefix*/).Dispose(); } } if (!_003C_003Ew__disposeMode) { _003C_003Es__20 = default(JsonElement.ArrayEnumerator); if (_003Csb_003E5__18.Length > 0) { _003Cparsed_003E5__14 = _003Csb_003E5__18.ToString(); } } goto IL_057d; } } finally { if (num == -1 && _003Cdoc_003E5__16 != null) { ((IDisposable)_003Cdoc_003E5__16).Dispose(); } } goto end_IL_03b3; IL_057d: if (!_003C_003Ew__disposeMode) { _003Cdoc_003E5__16 = null; _003Ccandidates_003E5__17 = default(JsonElement); _003Csb_003E5__18 = null; _003Cparts_003E5__19 = default(JsonElement); goto IL_05dc; } goto end_IL_0296; end_IL_03b3:; } catch (JsonException ex) { _003Cex_003E5__24 = ex; LogService.Warn("Gemini 스트리밍 JSON 파싱 오류: " + _003Cex_003E5__24.Message); goto IL_05dc; } } goto IL_062f; } goto end_IL_0275; IL_062f: if (!_003Creader_003E5__9.EndOfStream && !ct.IsCancellationRequested) { _003C_003E2__current = null; awaiter3 = ReadLineWithTimeoutAsync(_003Creader_003E5__9, ct).GetAwaiter(); if (!awaiter3.IsCompleted) { num = (_003C_003E1__state = 2); _003C_003Eu__3 = awaiter3; _003CStreamGeminiAsync_003Ed__43 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } goto IL_031d; } goto end_IL_0275; end_IL_0296:; } finally { if (num == -1 && _003Creader_003E5__9 != null) { ((IDisposable)_003Creader_003E5__9).Dispose(); } } if (!_003C_003Ew__disposeMode) { } goto IL_0699; end_IL_0275:; } finally { if (num == -1 && _003Cstream_003E5__8 != null) { ((IDisposable)_003Cstream_003E5__8).Dispose(); } } } finally { if (num == -1 && _003Cresp_003E5__7 != null) { ((IDisposable)_003Cresp_003E5__7).Dispose(); } } goto end_IL_0109; IL_06be: if (!_003C_003Ew__disposeMode) { } goto IL_06e3; end_IL_0109:; } finally { if (num == -1 && _003Creq_003E5__6 != null) { ((IDisposable)_003Creq_003E5__6).Dispose(); } } goto end_IL_0007; IL_06e3: if (!_003C_003Ew__disposeMode) { _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cmodel_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; } end_IL_0007:; } catch (Exception exception) { _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cmodel_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Cparsed_003E5__14 = null; _003C_003Es__15 = null; _003Cdoc_003E5__16 = null; _003Ccandidates_003E5__17 = default(JsonElement); _003Csb_003E5__18 = null; _003Cparts_003E5__19 = default(JsonElement); _003C_003Es__20 = default(JsonElement.ArrayEnumerator); _003Cpart_003E5__21 = default(JsonElement); _003Ct_003E5__22 = default(JsonElement); _003Ctext_003E5__23 = null; _003Cex_003E5__24 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetException(exception); return; } _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003CapiKey_003E5__2 = null; _003Cmodel_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Cparsed_003E5__14 = null; _003C_003Es__15 = null; _003Cdoc_003E5__16 = null; _003Ccandidates_003E5__17 = default(JsonElement); _003Csb_003E5__18 = null; _003Cparts_003E5__19 = default(JsonElement); _003C_003Es__20 = default(JsonElement.ArrayEnumerator); _003Cpart_003E5__21 = default(JsonElement); _003Ct_003E5__22 = default(JsonElement); _003Ctext_003E5__23 = null; _003Cex_003E5__24 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetResult(result: false); return; IL_0945: _003C_003Ev__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { _003CStreamGeminiAsync_003Ed__43 _003CStreamGeminiAsync_003Ed__; if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId) { _003C_003E1__state = -3; _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); _003C_003Ew__disposeMode = false; _003CStreamGeminiAsync_003Ed__ = this; } else { _003CStreamGeminiAsync_003Ed__ = new _003CStreamGeminiAsync_003Ed__43(-3) { _003C_003E4__this = _003C_003E4__this }; } _003CStreamGeminiAsync_003Ed__.messages = _003C_003E3__messages; if (_003C_003E3__ct.Equals(default(CancellationToken))) { _003CStreamGeminiAsync_003Ed__.ct = cancellationToken; } else if (cancellationToken.Equals(_003C_003E3__ct) || cancellationToken.Equals(default(CancellationToken))) { _003CStreamGeminiAsync_003Ed__.ct = _003C_003E3__ct; } else { _003C_003Ex__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(_003C_003E3__ct, cancellationToken); _003CStreamGeminiAsync_003Ed__.ct = _003C_003Ex__combinedTokens.Token; } return _003CStreamGeminiAsync_003Ed__; } [DebuggerHidden] ValueTask IAsyncEnumerator.MoveNextAsync() { if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamGeminiAsync_003Ed__43 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); short version = _003C_003Ev__promiseOfValueOrEnd.Version; if (_003C_003Ev__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask(_003C_003Ev__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask(this, version); } [DebuggerHidden] bool IValueTaskSource.GetResult(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (_003C_003E1__state >= -1) { throw new NotSupportedException(); } if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ew__disposeMode = true; _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamGeminiAsync_003Ed__43 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); return new ValueTask(this, _003C_003Ev__promiseOfValueOrEnd.Version); } } [CompilerGenerated] private sealed class _003CStreamOllamaAsync_003Ed__37 : IAsyncEnumerable, IAsyncEnumerator, IAsyncDisposable, IValueTaskSource, IValueTaskSource, IAsyncStateMachine { public int _003C_003E1__state; public AsyncIteratorMethodBuilder _003C_003Et__builder; public ManualResetValueTaskSourceCore _003C_003Ev__promiseOfValueOrEnd; private string _003C_003E2__current; private bool _003C_003Ew__disposeMode; private CancellationTokenSource _003C_003Ex__combinedTokens; private int _003C_003El__initialThreadId; private List messages; public List _003C_003E3__messages; private CancellationToken ct; public CancellationToken _003C_003E3__ct; public LlmService _003C_003E4__this; private LlmSettings _003Cllm_003E5__1; private string _003Cendpoint_003E5__2; private string _003Cep_003E5__3; private object _003Cbody_003E5__4; private string _003Curl_003E5__5; private HttpRequestMessage _003Creq_003E5__6; private HttpResponseMessage _003Cresp_003E5__7; private Stream _003Cstream_003E5__8; private StreamReader _003Creader_003E5__9; private HttpResponseMessage _003C_003Es__10; private Stream _003C_003Es__11; private string _003Cline_003E5__12; private string _003Ctext_003E5__13; private string _003C_003Es__14; private JsonDocument _003Cdoc_003E5__15; private JsonElement _003Cmsg_003E5__16; private JsonElement _003Cc_003E5__17; private JsonElement _003Cdone_003E5__18; private JsonException _003Cex_003E5__19; private TaskAwaiter _003C_003Eu__1; private TaskAwaiter _003C_003Eu__2; private TaskAwaiter _003C_003Eu__3; string IAsyncEnumerator.Current { [DebuggerHidden] get { return _003C_003E2__current; } } [DebuggerHidden] public _003CStreamOllamaAsync_003Ed__37(int _003C_003E1__state) { _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); this._003C_003E1__state = _003C_003E1__state; _003C_003El__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = _003C_003E1__state; try { switch (num) { default: if (!_003C_003Ew__disposeMode) { num = (_003C_003E1__state = -1); _003Cllm_003E5__1 = _003C_003E4__this._settings.Settings.Llm; _003Cendpoint_003E5__2 = _003C_003E4__this.ResolveServerInfo().Endpoint; _003Cep_003E5__3 = (string.IsNullOrEmpty(_003Cendpoint_003E5__2) ? _003Cllm_003E5__1.Endpoint : _003Cendpoint_003E5__2); _003Cbody_003E5__4 = _003C_003E4__this.BuildOllamaBody(messages, stream: true); _003Curl_003E5__5 = _003Cep_003E5__3.TrimEnd('/') + "/api/chat"; _003Creq_003E5__6 = new HttpRequestMessage(HttpMethod.Post, _003Curl_003E5__5) { Content = JsonContent(_003Cbody_003E5__4) }; break; } goto end_IL_0007; case -4: case 0: case 1: case 2: break; } try { TaskAwaiter awaiter; switch (num) { default: _003C_003E2__current = null; awaiter = _003C_003E4__this.SendWithErrorClassificationAsync(_003Creq_003E5__6, ct).GetAwaiter(); if (!awaiter.IsCompleted) { num = (_003C_003E1__state = 0); _003C_003Eu__1 = awaiter; _003CStreamOllamaAsync_003Ed__37 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } goto IL_01a1; case 0: awaiter = _003C_003Eu__1; _003C_003Eu__1 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_01a1; case -4: case 1: case 2: break; IL_01a1: _003C_003Es__10 = awaiter.GetResult(); _003Cresp_003E5__7 = _003C_003Es__10; _003C_003Es__10 = null; break; } try { if (num != -4) { TaskAwaiter awaiter2; if (num != 1) { if (num == 2) { goto IL_026f; } _003C_003E2__current = null; awaiter2 = _003Cresp_003E5__7.Content.ReadAsStreamAsync(ct).GetAwaiter(); if (!awaiter2.IsCompleted) { num = (_003C_003E1__state = 1); _003C_003Eu__2 = awaiter2; _003CStreamOllamaAsync_003Ed__37 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } } else { awaiter2 = _003C_003Eu__2; _003C_003Eu__2 = default(TaskAwaiter); num = (_003C_003E1__state = -1); } _003C_003Es__11 = awaiter2.GetResult(); _003Cstream_003E5__8 = _003C_003Es__11; _003C_003Es__11 = null; } goto IL_026f; IL_055b: if (!_003C_003Ew__disposeMode) { } goto IL_0580; IL_026f: try { if (num != -4 && num != 2) { _003Creader_003E5__9 = new StreamReader(_003Cstream_003E5__8); } try { TaskAwaiter awaiter3; if (num != -4) { if (num != 2) { goto IL_04f1; } awaiter3 = _003C_003Eu__3; _003C_003Eu__3 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_0317; } num = (_003C_003E1__state = -1); if (!_003C_003Ew__disposeMode) { goto IL_04e2; } goto end_IL_0290; IL_04a0: if (string.IsNullOrEmpty(_003Ctext_003E5__13)) { goto IL_04e2; } _003C_003E2__current = _003Ctext_003E5__13; num = (_003C_003E1__state = -4); goto IL_07ad; IL_04e2: _003Cline_003E5__12 = null; _003Ctext_003E5__13 = null; goto IL_04f1; IL_0317: _003C_003Es__14 = awaiter3.GetResult(); _003Cline_003E5__12 = _003C_003Es__14; _003C_003Es__14 = null; if (_003Cline_003E5__12 != null) { if (string.IsNullOrEmpty(_003Cline_003E5__12)) { goto IL_04f1; } _003Ctext_003E5__13 = null; try { _003Cdoc_003E5__15 = JsonDocument.Parse(_003Cline_003E5__12); try { if (_003Cdoc_003E5__15.RootElement.TryGetProperty("message", out _003Cmsg_003E5__16) && _003Cmsg_003E5__16.TryGetProperty("content", out _003Cc_003E5__17)) { _003Ctext_003E5__13 = _003Cc_003E5__17.GetString(); } if (_003Cdoc_003E5__15.RootElement.TryGetProperty("done", out _003Cdone_003E5__18) && _003Cdone_003E5__18.GetBoolean()) { _003C_003E4__this.TryParseOllamaUsage(_003Cdoc_003E5__15.RootElement); } } finally { if (num == -1 && _003Cdoc_003E5__15 != null) { ((IDisposable)_003Cdoc_003E5__15).Dispose(); } } if (!_003C_003Ew__disposeMode) { _003Cdoc_003E5__15 = null; _003Cmsg_003E5__16 = default(JsonElement); _003Cc_003E5__17 = default(JsonElement); _003Cdone_003E5__18 = default(JsonElement); goto IL_04a0; } } catch (JsonException ex) { _003Cex_003E5__19 = ex; LogService.Warn("Ollama 스트리밍 JSON 파싱 오류: " + _003Cex_003E5__19.Message); goto IL_04a0; } goto end_IL_0290; } goto end_IL_026f; IL_04f1: if (!_003Creader_003E5__9.EndOfStream && !ct.IsCancellationRequested) { _003C_003E2__current = null; awaiter3 = ReadLineWithTimeoutAsync(_003Creader_003E5__9, ct).GetAwaiter(); if (!awaiter3.IsCompleted) { num = (_003C_003E1__state = 2); _003C_003Eu__3 = awaiter3; _003CStreamOllamaAsync_003Ed__37 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } goto IL_0317; } goto end_IL_026f; end_IL_0290:; } finally { if (num == -1 && _003Creader_003E5__9 != null) { ((IDisposable)_003Creader_003E5__9).Dispose(); } } if (!_003C_003Ew__disposeMode) { } goto IL_055b; end_IL_026f:; } finally { if (num == -1 && _003Cstream_003E5__8 != null) { ((IDisposable)_003Cstream_003E5__8).Dispose(); } } } finally { if (num == -1 && _003Cresp_003E5__7 != null) { ((IDisposable)_003Cresp_003E5__7).Dispose(); } } goto end_IL_0103; IL_0580: if (!_003C_003Ew__disposeMode) { } goto IL_05a5; end_IL_0103:; } finally { if (num == -1 && _003Creq_003E5__6 != null) { ((IDisposable)_003Creq_003E5__6).Dispose(); } } goto end_IL_0007; IL_05a5: if (!_003C_003Ew__disposeMode) { _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; } end_IL_0007:; } catch (Exception exception) { _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Ctext_003E5__13 = null; _003C_003Es__14 = null; _003Cdoc_003E5__15 = null; _003Cmsg_003E5__16 = default(JsonElement); _003Cc_003E5__17 = default(JsonElement); _003Cdone_003E5__18 = default(JsonElement); _003Cex_003E5__19 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetException(exception); return; } _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Ctext_003E5__13 = null; _003C_003Es__14 = null; _003Cdoc_003E5__15 = null; _003Cmsg_003E5__16 = default(JsonElement); _003Cc_003E5__17 = default(JsonElement); _003Cdone_003E5__18 = default(JsonElement); _003Cex_003E5__19 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetResult(result: false); return; IL_07ad: _003C_003Ev__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { _003CStreamOllamaAsync_003Ed__37 _003CStreamOllamaAsync_003Ed__; if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId) { _003C_003E1__state = -3; _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); _003C_003Ew__disposeMode = false; _003CStreamOllamaAsync_003Ed__ = this; } else { _003CStreamOllamaAsync_003Ed__ = new _003CStreamOllamaAsync_003Ed__37(-3) { _003C_003E4__this = _003C_003E4__this }; } _003CStreamOllamaAsync_003Ed__.messages = _003C_003E3__messages; if (_003C_003E3__ct.Equals(default(CancellationToken))) { _003CStreamOllamaAsync_003Ed__.ct = cancellationToken; } else if (cancellationToken.Equals(_003C_003E3__ct) || cancellationToken.Equals(default(CancellationToken))) { _003CStreamOllamaAsync_003Ed__.ct = _003C_003E3__ct; } else { _003C_003Ex__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(_003C_003E3__ct, cancellationToken); _003CStreamOllamaAsync_003Ed__.ct = _003C_003Ex__combinedTokens.Token; } return _003CStreamOllamaAsync_003Ed__; } [DebuggerHidden] ValueTask IAsyncEnumerator.MoveNextAsync() { if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamOllamaAsync_003Ed__37 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); short version = _003C_003Ev__promiseOfValueOrEnd.Version; if (_003C_003Ev__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask(_003C_003Ev__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask(this, version); } [DebuggerHidden] bool IValueTaskSource.GetResult(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (_003C_003E1__state >= -1) { throw new NotSupportedException(); } if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ew__disposeMode = true; _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamOllamaAsync_003Ed__37 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); return new ValueTask(this, _003C_003Ev__promiseOfValueOrEnd.Version); } } [CompilerGenerated] private sealed class _003CStreamOpenAiCompatibleAsync_003Ed__40 : IAsyncEnumerable, IAsyncEnumerator, IAsyncDisposable, IValueTaskSource, IValueTaskSource, IAsyncStateMachine { public int _003C_003E1__state; public AsyncIteratorMethodBuilder _003C_003Et__builder; public ManualResetValueTaskSourceCore _003C_003Ev__promiseOfValueOrEnd; private string _003C_003E2__current; private bool _003C_003Ew__disposeMode; private CancellationTokenSource _003C_003Ex__combinedTokens; private int _003C_003El__initialThreadId; private List messages; public List _003C_003E3__messages; private CancellationToken ct; public CancellationToken _003C_003E3__ct; public LlmService _003C_003E4__this; private LlmSettings _003Cllm_003E5__1; private string _003Cendpoint_003E5__2; private string _003Cep_003E5__3; private object _003Cbody_003E5__4; private string _003Curl_003E5__5; private HttpRequestMessage _003Creq_003E5__6; private HttpResponseMessage _003Cresp_003E5__7; private Stream _003Cstream_003E5__8; private StreamReader _003Creader_003E5__9; private HttpResponseMessage _003C_003Es__10; private Stream _003C_003Es__11; private string _003Cline_003E5__12; private string _003Cdata_003E5__13; private string _003Ctext_003E5__14; private string _003C_003Es__15; private JsonDocument _003Cdoc_003E5__16; private JsonElement _003Cchoices_003E5__17; private JsonElement _003Cdelta_003E5__18; private JsonElement _003Cc_003E5__19; private JsonException _003Cex_003E5__20; private TaskAwaiter _003C_003Eu__1; private TaskAwaiter _003C_003Eu__2; private TaskAwaiter _003C_003Eu__3; private TaskAwaiter _003C_003Eu__4; string IAsyncEnumerator.Current { [DebuggerHidden] get { return _003C_003E2__current; } } [DebuggerHidden] public _003CStreamOpenAiCompatibleAsync_003Ed__40(int _003C_003E1__state) { _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); this._003C_003E1__state = _003C_003E1__state; _003C_003El__initialThreadId = Environment.CurrentManagedThreadId; } private void MoveNext() { int num = _003C_003E1__state; try { switch (num) { default: if (!_003C_003Ew__disposeMode) { num = (_003C_003E1__state = -1); _003Cllm_003E5__1 = _003C_003E4__this._settings.Settings.Llm; _003Cendpoint_003E5__2 = _003C_003E4__this.ResolveServerInfo().Endpoint; _003Cep_003E5__3 = (string.IsNullOrEmpty(_003Cendpoint_003E5__2) ? _003Cllm_003E5__1.Endpoint : _003Cendpoint_003E5__2); _003Cbody_003E5__4 = _003C_003E4__this.BuildOpenAiBody(messages, stream: true); _003Curl_003E5__5 = _003Cep_003E5__3.TrimEnd('/') + "/v1/chat/completions"; _003Creq_003E5__6 = new HttpRequestMessage(HttpMethod.Post, _003Curl_003E5__5) { Content = JsonContent(_003Cbody_003E5__4) }; break; } goto end_IL_0007; case -4: case 0: case 1: case 2: case 3: break; } try { TaskAwaiter awaiter2; TaskAwaiter awaiter; switch (num) { default: _003C_003E2__current = null; awaiter2 = _003C_003E4__this.ApplyAuthHeaderAsync(_003Creq_003E5__6, ct).GetAwaiter(); if (!awaiter2.IsCompleted) { num = (_003C_003E1__state = 0); _003C_003Eu__1 = awaiter2; _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } goto IL_01ae; case 0: awaiter2 = _003C_003Eu__1; _003C_003Eu__1 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_01ae; case 1: awaiter = _003C_003Eu__2; _003C_003Eu__2 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_0229; case -4: case 2: case 3: break; IL_0229: _003C_003Es__10 = awaiter.GetResult(); _003Cresp_003E5__7 = _003C_003Es__10; _003C_003Es__10 = null; break; IL_01ae: awaiter2.GetResult(); _003C_003E2__current = null; awaiter = _003C_003E4__this.SendWithErrorClassificationAsync(_003Creq_003E5__6, ct).GetAwaiter(); if (!awaiter.IsCompleted) { num = (_003C_003E1__state = 1); _003C_003Eu__2 = awaiter; _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } goto IL_0229; } try { if (num != -4) { TaskAwaiter awaiter3; if (num != 2) { if (num == 3) { goto IL_02f7; } _003C_003E2__current = null; awaiter3 = _003Cresp_003E5__7.Content.ReadAsStreamAsync(ct).GetAwaiter(); if (!awaiter3.IsCompleted) { num = (_003C_003E1__state = 2); _003C_003Eu__3 = awaiter3; _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } } else { awaiter3 = _003C_003Eu__3; _003C_003Eu__3 = default(TaskAwaiter); num = (_003C_003E1__state = -1); } _003C_003Es__11 = awaiter3.GetResult(); _003Cstream_003E5__8 = _003C_003Es__11; _003C_003Es__11 = null; } goto IL_02f7; IL_0646: if (!_003C_003Ew__disposeMode) { } goto IL_066b; IL_02f7: try { if (num != -4 && num != 3) { _003Creader_003E5__9 = new StreamReader(_003Cstream_003E5__8); } try { TaskAwaiter awaiter4; if (num != -4) { if (num != 3) { goto IL_05dc; } awaiter4 = _003C_003Eu__4; _003C_003Eu__4 = default(TaskAwaiter); num = (_003C_003E1__state = -1); goto IL_039f; } num = (_003C_003E1__state = -1); if (!_003C_003Ew__disposeMode) { goto IL_05c6; } goto end_IL_0318; IL_0584: if (string.IsNullOrEmpty(_003Ctext_003E5__14)) { goto IL_05c6; } _003C_003E2__current = _003Ctext_003E5__14; num = (_003C_003E1__state = -4); goto IL_08a6; IL_05c6: _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Ctext_003E5__14 = null; goto IL_05dc; IL_039f: _003C_003Es__15 = awaiter4.GetResult(); _003Cline_003E5__12 = _003C_003Es__15; _003C_003Es__15 = null; if (_003Cline_003E5__12 != null) { if (string.IsNullOrEmpty(_003Cline_003E5__12) || !_003Cline_003E5__12.StartsWith("data: ")) { goto IL_05dc; } string text = _003Cline_003E5__12; int length = "data: ".Length; _003Cdata_003E5__13 = text.Substring(length, text.Length - length); if (!(_003Cdata_003E5__13 == "[DONE]")) { _003Ctext_003E5__14 = null; try { _003Cdoc_003E5__16 = JsonDocument.Parse(_003Cdata_003E5__13); try { _003C_003E4__this.TryParseOpenAiUsage(_003Cdoc_003E5__16.RootElement); _003Cchoices_003E5__17 = _003Cdoc_003E5__16.RootElement.GetProperty("choices"); if (_003Cchoices_003E5__17.GetArrayLength() > 0) { _003Cdelta_003E5__18 = _003Cchoices_003E5__17[0].GetProperty("delta"); if (_003Cdelta_003E5__18.TryGetProperty("content", out _003Cc_003E5__19)) { _003Ctext_003E5__14 = _003Cc_003E5__19.GetString(); } _003Cdelta_003E5__18 = default(JsonElement); _003Cc_003E5__19 = default(JsonElement); } } finally { if (num == -1 && _003Cdoc_003E5__16 != null) { ((IDisposable)_003Cdoc_003E5__16).Dispose(); } } if (!_003C_003Ew__disposeMode) { _003Cdoc_003E5__16 = null; _003Cchoices_003E5__17 = default(JsonElement); goto IL_0584; } } catch (JsonException ex) { _003Cex_003E5__20 = ex; LogService.Warn("vLLM 스트리밍 JSON 파싱 오류: " + _003Cex_003E5__20.Message); goto IL_0584; } goto end_IL_0318; } } goto end_IL_02f7; IL_05dc: if (!_003Creader_003E5__9.EndOfStream && !ct.IsCancellationRequested) { _003C_003E2__current = null; awaiter4 = ReadLineWithTimeoutAsync(_003Creader_003E5__9, ct).GetAwaiter(); if (!awaiter4.IsCompleted) { num = (_003C_003E1__state = 3); _003C_003Eu__4 = awaiter4; _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.AwaitUnsafeOnCompleted(ref awaiter4, ref stateMachine); return; } goto IL_039f; } goto end_IL_02f7; end_IL_0318:; } finally { if (num == -1 && _003Creader_003E5__9 != null) { ((IDisposable)_003Creader_003E5__9).Dispose(); } } if (!_003C_003Ew__disposeMode) { } goto IL_0646; end_IL_02f7:; } finally { if (num == -1 && _003Cstream_003E5__8 != null) { ((IDisposable)_003Cstream_003E5__8).Dispose(); } } } finally { if (num == -1 && _003Cresp_003E5__7 != null) { ((IDisposable)_003Cresp_003E5__7).Dispose(); } } goto end_IL_0107; IL_066b: if (!_003C_003Ew__disposeMode) { } goto IL_0690; end_IL_0107:; } finally { if (num == -1 && _003Creq_003E5__6 != null) { ((IDisposable)_003Creq_003E5__6).Dispose(); } } goto end_IL_0007; IL_0690: if (!_003C_003Ew__disposeMode) { _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; } end_IL_0007:; } catch (Exception exception) { _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Ctext_003E5__14 = null; _003C_003Es__15 = null; _003Cdoc_003E5__16 = null; _003Cchoices_003E5__17 = default(JsonElement); _003Cdelta_003E5__18 = default(JsonElement); _003Cc_003E5__19 = default(JsonElement); _003Cex_003E5__20 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetException(exception); return; } _003C_003E1__state = -2; _003Cllm_003E5__1 = null; _003Cendpoint_003E5__2 = null; _003Cep_003E5__3 = null; _003Cbody_003E5__4 = null; _003Curl_003E5__5 = null; _003Creq_003E5__6 = null; _003Cresp_003E5__7 = null; _003Cstream_003E5__8 = null; _003Creader_003E5__9 = null; _003C_003Es__10 = null; _003C_003Es__11 = null; _003Cline_003E5__12 = null; _003Cdata_003E5__13 = null; _003Ctext_003E5__14 = null; _003C_003Es__15 = null; _003Cdoc_003E5__16 = null; _003Cchoices_003E5__17 = default(JsonElement); _003Cdelta_003E5__18 = default(JsonElement); _003Cc_003E5__19 = default(JsonElement); _003Cex_003E5__20 = null; if (_003C_003Ex__combinedTokens != null) { _003C_003Ex__combinedTokens.Dispose(); _003C_003Ex__combinedTokens = null; } _003C_003E2__current = null; _003C_003Et__builder.Complete(); _003C_003Ev__promiseOfValueOrEnd.SetResult(result: false); return; IL_08a6: _003C_003Ev__promiseOfValueOrEnd.SetResult(result: true); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } [DebuggerHidden] IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { _003CStreamOpenAiCompatibleAsync_003Ed__40 _003CStreamOpenAiCompatibleAsync_003Ed__; if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId) { _003C_003E1__state = -3; _003C_003Et__builder = AsyncIteratorMethodBuilder.Create(); _003C_003Ew__disposeMode = false; _003CStreamOpenAiCompatibleAsync_003Ed__ = this; } else { _003CStreamOpenAiCompatibleAsync_003Ed__ = new _003CStreamOpenAiCompatibleAsync_003Ed__40(-3) { _003C_003E4__this = _003C_003E4__this }; } _003CStreamOpenAiCompatibleAsync_003Ed__.messages = _003C_003E3__messages; if (_003C_003E3__ct.Equals(default(CancellationToken))) { _003CStreamOpenAiCompatibleAsync_003Ed__.ct = cancellationToken; } else if (cancellationToken.Equals(_003C_003E3__ct) || cancellationToken.Equals(default(CancellationToken))) { _003CStreamOpenAiCompatibleAsync_003Ed__.ct = _003C_003E3__ct; } else { _003C_003Ex__combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(_003C_003E3__ct, cancellationToken); _003CStreamOpenAiCompatibleAsync_003Ed__.ct = _003C_003Ex__combinedTokens.Token; } return _003CStreamOpenAiCompatibleAsync_003Ed__; } [DebuggerHidden] ValueTask IAsyncEnumerator.MoveNextAsync() { if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); short version = _003C_003Ev__promiseOfValueOrEnd.Version; if (_003C_003Ev__promiseOfValueOrEnd.GetStatus(version) == ValueTaskSourceStatus.Succeeded) { return new ValueTask(_003C_003Ev__promiseOfValueOrEnd.GetResult(version)); } return new ValueTask(this, version); } [DebuggerHidden] bool IValueTaskSource.GetResult(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] void IValueTaskSource.GetResult(short token) { _003C_003Ev__promiseOfValueOrEnd.GetResult(token); } [DebuggerHidden] ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) { return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token); } [DebuggerHidden] void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) { _003C_003Ev__promiseOfValueOrEnd.OnCompleted(continuation, state, token, flags); } [DebuggerHidden] ValueTask IAsyncDisposable.DisposeAsync() { if (_003C_003E1__state >= -1) { throw new NotSupportedException(); } if (_003C_003E1__state == -2) { return default(ValueTask); } _003C_003Ew__disposeMode = true; _003C_003Ev__promiseOfValueOrEnd.Reset(); _003CStreamOpenAiCompatibleAsync_003Ed__40 stateMachine = this; _003C_003Et__builder.MoveNext(ref stateMachine); return new ValueTask(this, _003C_003Ev__promiseOfValueOrEnd.Version); } } private readonly HttpClient _http; private readonly SettingsService _settings; private string? _systemPrompt; private const int MaxRetries = 2; private static readonly TimeSpan ChunkTimeout = TimeSpan.FromSeconds(30.0); private string? _serviceOverride; private string? _modelOverride; public TokenUsage? LastTokenUsage { get; private set; } public string? SystemPrompt => _systemPrompt; public string? LastFallbackInfo { get; private set; } public void PushRouteOverride(string service, string model) { _serviceOverride = service; _modelOverride = model; } public void ClearRouteOverride() { _serviceOverride = null; _modelOverride = null; } public (string service, string model) GetCurrentModelInfo() { return (service: ResolveService(), model: ResolveModel()); } private string ResolveService() { return _serviceOverride ?? _settings.Settings.Llm.Service; } private string ResolveModel() { if (_modelOverride != null) { return _modelOverride; } return ResolveModelName(); } private string ResolveApiKeyForService(string service) { LlmSettings llm = _settings.Settings.Llm; string text = service.ToLowerInvariant(); if (1 == 0) { } string result = text switch { "gemini" => llm.GeminiApiKey, "claude" => llm.ClaudeApiKey, "vllm" => CryptoService.DecryptIfEnabled(llm.VllmApiKey, llm.EncryptionEnabled), "ollama" => CryptoService.DecryptIfEnabled(llm.OllamaApiKey, llm.EncryptionEnabled), _ => "", }; if (1 == 0) { } return result; } private string ResolveEndpointForService(string service) { LlmSettings llm = _settings.Settings.Llm; string text = service.ToLowerInvariant(); if (1 == 0) { } string result = ((text == "vllm") ? llm.VllmEndpoint : ((!(text == "ollama")) ? llm.Endpoint : llm.OllamaEndpoint)); if (1 == 0) { } return result; } public LlmService(SettingsService settings) { _settings = settings; _http = new HttpClient { Timeout = TimeSpan.FromMinutes(10.0) }; LoadSystemPrompt(); } private void LoadSystemPrompt() { string baseDirectory = AppContext.BaseDirectory; string path = Path.Combine(baseDirectory, "system_prompt.txt"); if (File.Exists(path)) { _systemPrompt = File.ReadAllText(path, Encoding.UTF8).Trim(); } } private string ResolveModelName() { LlmSettings llm = _settings.Settings.Llm; string service = llm.Service; bool flag = ((service == "ollama" || service == "vllm") ? true : false); if (flag && !string.IsNullOrEmpty(llm.Model)) { return CryptoService.DecryptIfEnabled(llm.Model, llm.EncryptionEnabled); } return llm.Model; } private (string Endpoint, string ApiKey) ResolveServerInfo() { LlmSettings llm = _settings.Settings.Llm; string text = ResolveService(); string modelName = ResolveModel(); RegisteredModel registeredModel = FindRegisteredModel(llm, text, modelName); if (registeredModel != null && !string.IsNullOrEmpty(registeredModel.Endpoint)) { string item = ((!string.IsNullOrEmpty(registeredModel.ApiKey)) ? CryptoService.DecryptIfEnabled(registeredModel.ApiKey, llm.EncryptionEnabled) : GetDefaultApiKey(llm, text)); return (Endpoint: registeredModel.Endpoint, ApiKey: item); } string text2 = text.ToLowerInvariant(); if (1 == 0) { } (string, string) result = ((text2 == "vllm") ? (llm.VllmEndpoint, CryptoService.DecryptIfEnabled(llm.VllmApiKey, llm.EncryptionEnabled)) : ((!(text2 == "ollama")) ? ("", "") : (llm.OllamaEndpoint, CryptoService.DecryptIfEnabled(llm.OllamaApiKey, llm.EncryptionEnabled)))); if (1 == 0) { } return result; } private static RegisteredModel? FindRegisteredModel(LlmSettings llm, string service, string modelName) { return llm.RegisteredModels.FirstOrDefault((RegisteredModel m) => m.Service.Equals(service, StringComparison.OrdinalIgnoreCase) && (CryptoService.DecryptIfEnabled(m.EncryptedModelName, llm.EncryptionEnabled) == modelName || m.Alias == modelName)); } internal async Task ResolveAuthTokenAsync(CancellationToken ct = default(CancellationToken)) { LlmSettings llm = _settings.Settings.Llm; string activeService = ResolveService(); string modelName = ResolveModel(); RegisteredModel registered = FindRegisteredModel(llm, activeService, modelName); if (registered != null && registered.AuthType.Equals("cp4d", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(registered.Cp4dUrl)) { return await Cp4dTokenService.GetTokenAsync(password: CryptoService.DecryptIfEnabled(registered.Cp4dPassword, llm.EncryptionEnabled), cp4dUrl: registered.Cp4dUrl, username: registered.Cp4dUsername, ct: ct); } string apiKey = ResolveServerInfo().ApiKey; return string.IsNullOrEmpty(apiKey) ? null : apiKey; } private async Task ApplyAuthHeaderAsync(HttpRequestMessage req, CancellationToken ct) { string token = await ResolveAuthTokenAsync(ct); if (!string.IsNullOrEmpty(token)) { req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } } private static string GetDefaultApiKey(LlmSettings llm, string? service = null) { string text = service ?? llm.Service; string text2 = text.ToLowerInvariant(); if (1 == 0) { } string result = ((text2 == "vllm") ? CryptoService.DecryptIfEnabled(llm.VllmApiKey, llm.EncryptionEnabled) : ((!(text2 == "ollama")) ? "" : CryptoService.DecryptIfEnabled(llm.OllamaApiKey, llm.EncryptionEnabled))); if (1 == 0) { } return result; } public async Task SendAsync(List messages, CancellationToken ct = default(CancellationToken)) { LlmSettings llm = _settings.Settings.Llm; string activeService = ResolveService(); try { return await SendWithServiceAsync(activeService, messages, ct); } catch (Exception ex) when (llm.FallbackModels.Count > 0) { foreach (string fallback in llm.FallbackModels) { string[] parts = fallback.Split(':', 2); string fbService = parts[0].Trim(); if (parts.Length > 1) { parts[1].Trim(); } try { LogService.Warn($"모델 폴백: {activeService} → {fbService} ({ex.Message})"); LastFallbackInfo = activeService + " → " + fbService; return await SendWithServiceAsync(fbService, messages, ct); } catch { } } throw; } } private Task SendWithServiceAsync(string service, List messages, CancellationToken ct) { string text = service.ToLowerInvariant(); if (1 == 0) { } Task result = text switch { "gemini" => SendGeminiAsync(messages, ct), "claude" => SendClaudeAsync(messages, ct), "vllm" => SendOpenAiCompatibleAsync(messages, ct), _ => SendOllamaAsync(messages, ct), }; if (1 == 0) { } return result; } [AsyncIteratorStateMachine(typeof(_003CStreamAsync_003Ed__34))] public IAsyncEnumerable StreamAsync(List messages, [EnumeratorCancellation] CancellationToken ct = default(CancellationToken)) { return new _003CStreamAsync_003Ed__34(-2) { _003C_003E4__this = this, _003C_003E3__messages = messages, _003C_003E3__ct = ct }; } public async Task<(bool ok, string message)> TestConnectionAsync() { try { LlmSettings llm = _settings.Settings.Llm; switch (llm.Service.ToLowerInvariant()) { case "ollama": { HttpResponseMessage resp = await _http.GetAsync(llm.Endpoint.TrimEnd('/') + "/api/tags"); return resp.IsSuccessStatusCode ? (ok: true, message: "Ollama 연결 성공") : (ok: false, message: ClassifyHttpError(resp)); } case "vllm": { HttpResponseMessage vResp = await _http.GetAsync(llm.Endpoint.TrimEnd('/') + "/v1/models"); return vResp.IsSuccessStatusCode ? (ok: true, message: "vLLM 연결 성공") : (ok: false, message: ClassifyHttpError(vResp)); } case "gemini": { string gKey = llm.ApiKey; if (string.IsNullOrEmpty(gKey)) { return (ok: false, message: "API 키가 설정되지 않았습니다"); } HttpResponseMessage gResp = await _http.GetAsync("https://generativelanguage.googleapis.com/v1beta/models?key=" + gKey); return gResp.IsSuccessStatusCode ? (ok: true, message: "Gemini API 연결 성공") : (ok: false, message: ClassifyHttpError(gResp)); } case "claude": { string cKey = llm.ApiKey; if (string.IsNullOrEmpty(cKey)) { return (ok: false, message: "API 키가 설정되지 않았습니다"); } using HttpRequestMessage cReq = new HttpRequestMessage(HttpMethod.Get, "https://api.anthropic.com/v1/models"); cReq.Headers.Add("x-api-key", cKey); cReq.Headers.Add("anthropic-version", "2023-06-01"); HttpResponseMessage cResp = await _http.SendAsync(cReq); return cResp.IsSuccessStatusCode ? (ok: true, message: "Claude API 연결 성공") : (ok: false, message: ClassifyHttpError(cResp)); } default: return (ok: false, message: "알 수 없는 서비스"); } } catch (TaskCanceledException) { return (ok: false, message: "연결 시간 초과 — 서버가 응답하지 않습니다"); } catch (HttpRequestException ex2) { HttpRequestException ex3 = ex2; return (ok: false, message: "연결 실패 — " + ex3.Message); } catch (Exception ex4) { Exception ex5 = ex4; return (ok: false, message: ex5.Message); } } private async Task SendOllamaAsync(List messages, CancellationToken ct) { LlmSettings llm = _settings.Settings.Llm; string endpoint = ResolveServerInfo().Endpoint; string ep = (string.IsNullOrEmpty(endpoint) ? llm.Endpoint : endpoint); return SafeParseJson(await PostJsonWithRetryAsync(body: BuildOllamaBody(messages, stream: false), url: ep.TrimEnd('/') + "/api/chat", ct: ct), delegate(JsonElement root) { TryParseOllamaUsage(root); return root.GetProperty("message").GetProperty("content").GetString() ?? ""; }, "Ollama 응답"); } [AsyncIteratorStateMachine(typeof(_003CStreamOllamaAsync_003Ed__37))] private IAsyncEnumerable StreamOllamaAsync(List messages, [EnumeratorCancellation] CancellationToken ct) { return new _003CStreamOllamaAsync_003Ed__37(-2) { _003C_003E4__this = this, _003C_003E3__messages = messages, _003C_003E3__ct = ct }; } private object BuildOllamaBody(List messages, bool stream) { LlmSettings llm = _settings.Settings.Llm; List messages2 = BuildMessageList(messages); return new { model = ResolveModelName(), messages = messages2, stream = stream, options = new { temperature = llm.Temperature } }; } private async Task SendOpenAiCompatibleAsync(List messages, CancellationToken ct) { LlmSettings llm = _settings.Settings.Llm; string endpoint = ResolveServerInfo().Endpoint; string ep = (string.IsNullOrEmpty(endpoint) ? llm.Endpoint : endpoint); object body = BuildOpenAiBody(messages, stream: false); string url = ep.TrimEnd('/') + "/v1/chat/completions"; string json = JsonSerializer.Serialize(body); using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; await ApplyAuthHeaderAsync(req, ct); using HttpResponseMessage resp = await SendWithErrorClassificationAsync(req, ct); return SafeParseJson(await resp.Content.ReadAsStringAsync(ct), delegate(JsonElement root) { TryParseOpenAiUsage(root); JsonElement property = root.GetProperty("choices"); return (property.GetArrayLength() == 0) ? "(빈 응답)" : (property[0].GetProperty("message").GetProperty("content").GetString() ?? ""); }, "vLLM 응답"); } [AsyncIteratorStateMachine(typeof(_003CStreamOpenAiCompatibleAsync_003Ed__40))] private IAsyncEnumerable StreamOpenAiCompatibleAsync(List messages, [EnumeratorCancellation] CancellationToken ct) { return new _003CStreamOpenAiCompatibleAsync_003Ed__40(-2) { _003C_003E4__this = this, _003C_003E3__messages = messages, _003C_003E3__ct = ct }; } private object BuildOpenAiBody(List messages, bool stream) { LlmSettings llm = _settings.Settings.Llm; List messages2 = BuildMessageList(messages, openAiVision: true); return new { model = ResolveModelName(), messages = messages2, stream = stream, temperature = llm.Temperature, max_tokens = llm.MaxContextTokens }; } private async Task SendGeminiAsync(List messages, CancellationToken ct) { _ = _settings.Settings.Llm; string apiKey = ResolveApiKeyForService("gemini"); if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("Gemini API 키가 설정되지 않았습니다. 설정 > AX Agent에서 API 키를 입력하세요."); } string model = ResolveModel(); object body = BuildGeminiBody(messages); string url = "https://generativelanguage.googleapis.com/v1beta/models/" + model + ":generateContent?key=" + apiKey; return SafeParseJson(await PostJsonWithRetryAsync(url, body, ct), delegate(JsonElement root) { TryParseGeminiUsage(root); JsonElement property = root.GetProperty("candidates"); if (property.GetArrayLength() == 0) { return "(빈 응답)"; } JsonElement property2 = property[0].GetProperty("content").GetProperty("parts"); return (property2.GetArrayLength() == 0) ? "(빈 응답)" : (property2[0].GetProperty("text").GetString() ?? ""); }, "Gemini 응답"); } [AsyncIteratorStateMachine(typeof(_003CStreamGeminiAsync_003Ed__43))] private IAsyncEnumerable StreamGeminiAsync(List messages, [EnumeratorCancellation] CancellationToken ct) { return new _003CStreamGeminiAsync_003Ed__43(-2) { _003C_003E4__this = this, _003C_003E3__messages = messages, _003C_003E3__ct = ct }; } private object BuildGeminiBody(List messages) { LlmSettings llm = _settings.Settings.Llm; List list = new List(); object obj = null; if (!string.IsNullOrEmpty(_systemPrompt)) { obj = new { parts = new[] { new { text = _systemPrompt } } }; } foreach (ChatMessage message in messages) { if (message.Role == "system") { continue; } List list2 = new List { new { text = message.Content } }; List? images = message.Images; if (images != null && images.Count > 0) { foreach (ImageAttachment image in message.Images) { list2.Add(new { inlineData = new { mimeType = image.MimeType, data = image.Base64 } }); } } list.Add(new { role = ((message.Role == "assistant") ? "model" : "user"), parts = list2 }); } if (obj != null) { return new { systemInstruction = obj, contents = list, generationConfig = new { temperature = llm.Temperature, maxOutputTokens = llm.MaxContextTokens } }; } return new { contents = list, generationConfig = new { temperature = llm.Temperature, maxOutputTokens = llm.MaxContextTokens } }; } private async Task SendClaudeAsync(List messages, CancellationToken ct) { LlmSettings llm = _settings.Settings.Llm; string apiKey = llm.ApiKey; if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("Claude API 키가 설정되지 않았습니다. 설정 > AX Agent에서 API 키를 입력하세요."); } object body = BuildClaudeBody(messages, stream: false); string json = JsonSerializer.Serialize(body); using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "https://api.anthropic.com/v1/messages"); req.Content = new StringContent(json, Encoding.UTF8, "application/json"); req.Headers.Add("x-api-key", apiKey); req.Headers.Add("anthropic-version", "2023-06-01"); using HttpResponseMessage resp = await _http.SendAsync(req, ct); if (!resp.IsSuccessStatusCode) { throw new HttpRequestException(ClassifyHttpError(resp, await resp.Content.ReadAsStringAsync(ct))); } return SafeParseJson(await resp.Content.ReadAsStringAsync(ct), delegate(JsonElement root) { TryParseClaudeUsage(root); JsonElement property = root.GetProperty("content"); return (property.GetArrayLength() == 0) ? "(빈 응답)" : (property[0].GetProperty("text").GetString() ?? ""); }, "Claude 응답"); } [AsyncIteratorStateMachine(typeof(_003CStreamClaudeAsync_003Ed__46))] private IAsyncEnumerable StreamClaudeAsync(List messages, [EnumeratorCancellation] CancellationToken ct) { return new _003CStreamClaudeAsync_003Ed__46(-2) { _003C_003E4__this = this, _003C_003E3__messages = messages, _003C_003E3__ct = ct }; } private object BuildClaudeBody(List messages, bool stream) { LlmSettings llm = _settings.Settings.Llm; List list = new List(); foreach (ChatMessage message in messages) { if (message.Role == "system") { continue; } List? images = message.Images; if (images != null && images.Count > 0) { List list2 = new List(); foreach (ImageAttachment image in message.Images) { list2.Add(new { type = "image", source = new { type = "base64", media_type = image.MimeType, data = image.Base64 } }); } list2.Add(new { type = "text", text = message.Content }); list.Add(new { role = message.Role, content = list2 }); } else { list.Add(new { role = message.Role, content = message.Content }); } } string model = ResolveModel(); if (!string.IsNullOrEmpty(_systemPrompt)) { return new { model = model, max_tokens = llm.MaxContextTokens, temperature = llm.Temperature, system = _systemPrompt, messages = list, stream = stream }; } return new { model = model, max_tokens = llm.MaxContextTokens, temperature = llm.Temperature, messages = list, stream = stream }; } private List BuildMessageList(List messages, bool openAiVision = false) { List list = new List(); if (!string.IsNullOrEmpty(_systemPrompt)) { list.Add(new { role = "system", content = _systemPrompt }); } foreach (ChatMessage message in messages) { if (message.Role == "system") { continue; } List? images = message.Images; if (images != null && images.Count > 0) { if (openAiVision) { List list2 = new List(); list2.Add(new { type = "text", text = message.Content }); foreach (ImageAttachment image in message.Images) { list2.Add(new { type = "image_url", image_url = new { url = "data:" + image.MimeType + ";base64," + image.Base64 } }); } list.Add(new { role = message.Role, content = list2 }); } else { list.Add(new { role = message.Role, content = message.Content, images = message.Images.Select((ImageAttachment i) => i.Base64).ToArray() }); } } else { list.Add(new { role = message.Role, content = message.Content }); } } return list; } private async Task PostJsonWithRetryAsync(string url, object body, CancellationToken ct) { string json = JsonSerializer.Serialize(body); Exception lastEx = null; for (int attempt = 0; attempt <= 2; attempt++) { try { using StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); using HttpResponseMessage resp = await _http.PostAsync(url, content, ct); if (resp.IsSuccessStatusCode) { return await resp.Content.ReadAsStringAsync(ct); } if (resp.StatusCode != HttpStatusCode.TooManyRequests || attempt >= 2) { throw new HttpRequestException(ClassifyHttpError(resp, await resp.Content.ReadAsStringAsync(ct))); } await Task.Delay(1000 * (attempt + 1), ct); } catch (HttpRequestException) { throw; } catch (TaskCanceledException) when (!ct.IsCancellationRequested && attempt < 2) { lastEx = new TimeoutException("요청 시간 초과"); await Task.Delay(1000 * (attempt + 1), ct); } } throw lastEx ?? new HttpRequestException("요청 실패"); } private async Task SendWithErrorClassificationAsync(HttpRequestMessage req, CancellationToken ct) { HttpResponseMessage resp = await _http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct); if (!resp.IsSuccessStatusCode) { string errorMsg = ClassifyHttpError(resp, await resp.Content.ReadAsStringAsync(ct)); resp.Dispose(); throw new HttpRequestException(errorMsg); } return resp; } private static async Task ReadLineWithTimeoutAsync(StreamReader reader, CancellationToken ct) { using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct); cts.CancelAfter(ChunkTimeout); try { return await reader.ReadLineAsync(cts.Token); } catch (OperationCanceledException) when (!ct.IsCancellationRequested) { LogService.Warn("스트리밍 청크 타임아웃 (30초 무응답)"); return null; } } private static string SafeParseJson(string json, Func extractor, string context) { try { using JsonDocument jsonDocument = JsonDocument.Parse(json); if (jsonDocument.RootElement.TryGetProperty("error", out var value)) { JsonElement value2; string text = (value.TryGetProperty("message", out value2) ? value2.GetString() : value.ToString()); throw new HttpRequestException("[" + context + "] API 에러: " + text); } return extractor(jsonDocument.RootElement); } catch (JsonException ex) { string value3 = ((json.Length > 200) ? (json.Substring(0, 200) + "…") : json); throw new InvalidOperationException($"[{context}] 응답 형식 오류 — 예상하지 못한 JSON 형식입니다.\n파싱 오류: {ex.Message}\n응답 미리보기: {value3}"); } catch (KeyNotFoundException) { string text2 = ((json.Length > 200) ? (json.Substring(0, 200) + "…") : json); throw new InvalidOperationException("[" + context + "] 응답에 필요한 필드가 없습니다.\n응답 미리보기: " + text2); } } private static string ClassifyHttpError(HttpResponseMessage resp, string? body = null) { int statusCode = (int)resp.StatusCode; string text = ""; if (!string.IsNullOrEmpty(body)) { try { using JsonDocument jsonDocument = JsonDocument.Parse(body); if (jsonDocument.RootElement.TryGetProperty("error", out var value)) { if (value.ValueKind == JsonValueKind.Object && value.TryGetProperty("message", out var value2)) { text = value2.GetString() ?? ""; } else if (value.ValueKind == JsonValueKind.String) { text = value.GetString() ?? ""; } } } catch { } } if (1 == 0) { } string text2; switch (statusCode) { case 400: text2 = "잘못된 요청 — 모델 이름이나 요청 형식을 확인하세요"; break; case 401: text2 = "인증 실패 — API 키가 유효하지 않습니다"; break; case 403: text2 = "접근 거부 — API 키 권한을 확인하세요"; break; case 404: text2 = "모델을 찾을 수 없습니다 — 모델 이름을 확인하세요"; break; case 429: text2 = "요청 한도 초과 — 잠시 후 다시 시도하세요"; break; case 500: text2 = "서버 내부 오류 — LLM 서버 상태를 확인하세요"; break; case 502: case 503: text2 = "서버 일시 장애 — 잠시 후 다시 시도하세요"; break; default: text2 = $"HTTP {statusCode} 오류"; break; } if (1 == 0) { } string text3 = text2; return string.IsNullOrEmpty(text) ? text3 : (text3 + "\n상세: " + text); } private static StringContent JsonContent(object body) { string content = JsonSerializer.Serialize(body); return new StringContent(content, Encoding.UTF8, "application/json"); } private void TryParseOllamaUsage(JsonElement root) { try { JsonElement value; int num = (root.TryGetProperty("prompt_eval_count", out value) ? value.GetInt32() : 0); JsonElement value2; int num2 = (root.TryGetProperty("eval_count", out value2) ? value2.GetInt32() : 0); if (num > 0 || num2 > 0) { LastTokenUsage = new TokenUsage(num, num2); } } catch { } } private void TryParseOpenAiUsage(JsonElement root) { try { if (root.TryGetProperty("usage", out var value)) { JsonElement value2; int num = (value.TryGetProperty("prompt_tokens", out value2) ? value2.GetInt32() : 0); JsonElement value3; int num2 = (value.TryGetProperty("completion_tokens", out value3) ? value3.GetInt32() : 0); if (num > 0 || num2 > 0) { LastTokenUsage = new TokenUsage(num, num2); } } } catch { } } private void TryParseGeminiUsage(JsonElement root) { try { if (root.TryGetProperty("usageMetadata", out var value)) { JsonElement value2; int num = (value.TryGetProperty("promptTokenCount", out value2) ? value2.GetInt32() : 0); JsonElement value3; int num2 = (value.TryGetProperty("candidatesTokenCount", out value3) ? value3.GetInt32() : 0); if (num > 0 || num2 > 0) { LastTokenUsage = new TokenUsage(num, num2); } } } catch { } } private void TryParseClaudeUsage(JsonElement root) { try { if (root.TryGetProperty("usage", out var value)) { TryParseClaudeUsageFromElement(value); } } catch { } } private void TryParseClaudeUsageFromElement(JsonElement usage) { try { JsonElement value; int num = (usage.TryGetProperty("input_tokens", out value) ? value.GetInt32() : 0); JsonElement value2; int num2 = (usage.TryGetProperty("output_tokens", out value2) ? value2.GetInt32() : 0); if (num > 0 || num2 > 0) { LastTokenUsage = new TokenUsage(num, num2); } } catch { } } public void Dispose() { _http.Dispose(); } public async Task> SendWithToolsAsync(List messages, IReadOnlyCollection tools, CancellationToken ct = default(CancellationToken)) { string activeService = ResolveService(); string text = activeService.ToLowerInvariant(); if (1 == 0) { } List result; switch (text) { case "claude": result = await SendClaudeWithToolsAsync(messages, tools, ct); break; case "gemini": result = await SendGeminiWithToolsAsync(messages, tools, ct); break; case "ollama": case "vllm": result = await SendOpenAiWithToolsAsync(messages, tools, ct); break; default: throw new NotSupportedException("서비스 '" + activeService + "'는 아직 Function Calling을 지원하지 않습니다."); } if (1 == 0) { } return result; } public static ChatMessage CreateToolResultMessage(string toolId, string toolName, string result) { string content = JsonSerializer.Serialize(new { type = "tool_result", tool_use_id = toolId, tool_name = toolName, content = result }); return new ChatMessage { Role = "user", Content = content, Timestamp = DateTime.Now }; } private async Task> SendClaudeWithToolsAsync(List messages, IReadOnlyCollection tools, CancellationToken ct) { LlmSettings llm = _settings.Settings.Llm; string apiKey = llm.ApiKey; if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("Claude API 키가 설정되지 않았습니다."); } object body = BuildClaudeToolBody(messages, tools); string json = JsonSerializer.Serialize(body); using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "https://api.anthropic.com/v1/messages"); req.Content = new StringContent(json, Encoding.UTF8, "application/json"); req.Headers.Add("x-api-key", apiKey); req.Headers.Add("anthropic-version", "2023-06-01"); using HttpResponseMessage resp = await _http.SendAsync(req, ct); if (!resp.IsSuccessStatusCode) { throw new HttpRequestException(ClassifyHttpError(resp, await resp.Content.ReadAsStringAsync(ct))); } using JsonDocument doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync(ct)); JsonElement root = doc.RootElement; if (root.TryGetProperty("usage", out var usage)) { TryParseClaudeUsageFromElement(usage); } List blocks = new List(); if (root.TryGetProperty("content", out var content)) { foreach (JsonElement block in content.EnumerateArray()) { JsonElement tp; string type = (block.TryGetProperty("type", out tp) ? tp.GetString() : ""); if (type == "text") { blocks.Add(new ContentBlock { Type = "text", Text = (block.TryGetProperty("text", out var txt) ? (txt.GetString() ?? "") : "") }); txt = default(JsonElement); } else if (type == "tool_use") { 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) ? new JsonElement?(inp.Clone()) : ((JsonElement?)null)) }); nm = default(JsonElement); bid = default(JsonElement); inp = default(JsonElement); } tp = default(JsonElement); } } return blocks; } private object BuildClaudeToolBody(List messages, IReadOnlyCollection tools) { LlmSettings llm = _settings.Settings.Llm; List list = new List(); foreach (ChatMessage message in messages) { if (message.Role == "system") { continue; } if (message.Role == "user" && message.Content.StartsWith("{\"type\":\"tool_result\"")) { try { using JsonDocument jsonDocument = JsonDocument.Parse(message.Content); JsonElement rootElement = jsonDocument.RootElement; list.Add(new { role = "user", content = new object[1] { new { type = "tool_result", tool_use_id = (rootElement.TryGetProperty("tool_use_id", out var value) ? value.GetString() : ""), content = (rootElement.TryGetProperty("content", out var value2) ? value2.GetString() : "") } } }); } catch { goto IL_0117; } continue; } goto IL_0117; IL_0117: if (message.Role == "assistant" && message.Content.StartsWith("{\"_tool_use_blocks\"")) { try { using JsonDocument jsonDocument2 = JsonDocument.Parse(message.Content); if (!jsonDocument2.RootElement.TryGetProperty("_tool_use_blocks", out var value3)) { throw new Exception(); } List list2 = new List(); foreach (JsonElement item in value3.EnumerateArray()) { JsonElement value4; string text = (item.TryGetProperty("type", out value4) ? value4.GetString() : ""); if (text == "text") { list2.Add(new { type = "text", text = (item.TryGetProperty("text", out var value5) ? (value5.GetString() ?? "") : "") }); } else if (text == "tool_use") { list2.Add(new { type = "tool_use", id = (item.TryGetProperty("id", out var value6) ? (value6.GetString() ?? "") : ""), name = (item.TryGetProperty("name", out var value7) ? (value7.GetString() ?? "") : ""), input = (item.TryGetProperty("input", out var value8) ? ((object)value8.Clone()) : new { }) }); } } list.Add(new { role = "assistant", content = list2 }); } catch { goto IL_0301; } continue; } goto IL_0301; IL_0301: List? images = message.Images; if (images != null && images.Count > 0 && message.Role == "user") { List list3 = new List(); foreach (ImageAttachment image in message.Images) { list3.Add(new { type = "image", source = new { type = "base64", media_type = image.MimeType, data = image.Base64 } }); } list3.Add(new { type = "text", text = message.Content }); list.Add(new { role = message.Role, content = list3 }); } else { list.Add(new { role = message.Role, content = message.Content }); } } var tools2 = tools.Select((IAgentTool t) => new { name = t.Name, description = t.Description, input_schema = new { type = "object", properties = t.Parameters.Properties.ToDictionary, string, object>((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => BuildPropertySchema(kv.Value, upperCaseType: false)), required = t.Parameters.Required } }).ToArray(); string text2 = messages.FirstOrDefault((ChatMessage m) => m.Role == "system")?.Content ?? _systemPrompt; string model = ResolveModel(); if (!string.IsNullOrEmpty(text2)) { return new { model = model, max_tokens = Math.Max(llm.MaxContextTokens, 4096), temperature = llm.Temperature, system = text2, messages = list, tools = tools2, stream = false }; } return new { model = model, max_tokens = Math.Max(llm.MaxContextTokens, 4096), temperature = llm.Temperature, messages = list, tools = tools2, stream = false }; } private async Task> SendGeminiWithToolsAsync(List messages, IReadOnlyCollection tools, CancellationToken ct) { _ = _settings.Settings.Llm; string apiKey = ResolveApiKeyForService("gemini"); if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("Gemini API 키가 설정되지 않았습니다."); } string activeModel = ResolveModel(); object body = BuildGeminiToolBody(messages, tools); string url = "https://generativelanguage.googleapis.com/v1beta/models/" + activeModel + ":generateContent?key=" + apiKey; string json = JsonSerializer.Serialize(body); using StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); using HttpResponseMessage resp = await _http.PostAsync(url, content, ct); if (!resp.IsSuccessStatusCode) { string errBody = await resp.Content.ReadAsStringAsync(ct); throw new HttpRequestException($"Gemini API 오류 ({resp.StatusCode}): {errBody}"); } using JsonDocument doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync(ct)); JsonElement root = doc.RootElement; TryParseGeminiUsage(root); List blocks = new List(); if (root.TryGetProperty("candidates", out var candidates) && candidates.GetArrayLength() > 0 && candidates[0].TryGetProperty("content", out var contentObj) && contentObj.TryGetProperty("parts", out var parts)) { foreach (JsonElement part in parts.EnumerateArray()) { if (part.TryGetProperty("text", out var text)) { blocks.Add(new ContentBlock { Type = "text", Text = (text.GetString() ?? "") }); } else { if (part.TryGetProperty("functionCall", out var fc)) { blocks.Add(new ContentBlock { Type = "tool_use", ToolName = (fc.TryGetProperty("name", out var fcName) ? (fcName.GetString() ?? "") : ""), ToolId = Guid.NewGuid().ToString("N").Substring(0, 12), ToolInput = (fc.TryGetProperty("args", out var a) ? new JsonElement?(a.Clone()) : ((JsonElement?)null)) }); fcName = default(JsonElement); a = default(JsonElement); } fc = default(JsonElement); } text = default(JsonElement); } } return blocks; } private object BuildGeminiToolBody(List messages, IReadOnlyCollection tools) { List list = new List(); foreach (ChatMessage message in messages) { if (message.Role == "system") { continue; } string role = ((message.Role == "assistant") ? "model" : "user"); if (message.Role == "user" && message.Content.StartsWith("{\"type\":\"tool_result\"")) { try { using JsonDocument jsonDocument = JsonDocument.Parse(message.Content); JsonElement rootElement = jsonDocument.RootElement; JsonElement value; string name = (rootElement.TryGetProperty("tool_name", out value) ? (value.GetString() ?? "") : ""); JsonElement value2; string result = (rootElement.TryGetProperty("content", out value2) ? (value2.GetString() ?? "") : ""); list.Add(new { role = "function", parts = new object[1] { new { functionResponse = new { name = name, response = new { result } } } } }); } catch { goto IL_0146; } continue; } goto IL_0146; IL_0146: if (message.Role == "assistant" && message.Content.StartsWith("{\"_tool_use_blocks\"")) { try { using JsonDocument jsonDocument2 = JsonDocument.Parse(message.Content); if (jsonDocument2.RootElement.TryGetProperty("_tool_use_blocks", out var value3)) { List list2 = new List(); foreach (JsonElement item in value3.EnumerateArray()) { JsonElement value4; string text = (item.TryGetProperty("type", out value4) ? value4.GetString() : ""); if (text == "text") { list2.Add(new { text = (item.TryGetProperty("text", out var value5) ? (value5.GetString() ?? "") : "") }); } else if (text == "tool_use") { list2.Add(new { functionCall = new { name = (item.TryGetProperty("name", out var value6) ? (value6.GetString() ?? "") : ""), args = (item.TryGetProperty("input", out var value7) ? ((object)value7.Clone()) : new { }) } }); } } list.Add(new { role = "model", parts = list2 }); continue; } } catch { } } List? images = message.Images; if (images != null && images.Count > 0 && message.Role == "user") { List list3 = new List { new { text = message.Content } }; foreach (ImageAttachment image in message.Images) { list3.Add(new { inlineData = new { mimeType = image.MimeType, data = image.Base64 } }); } list.Add(new { role = role, parts = list3 }); } else { list.Add(new { role = role, parts = new[] { new { text = message.Content } } }); } } var function_declarations = tools.Select((IAgentTool t) => new { name = t.Name, description = t.Description, parameters = new { type = "OBJECT", properties = t.Parameters.Properties.ToDictionary, string, object>((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => BuildPropertySchema(kv.Value, upperCaseType: true)), required = t.Parameters.Required } }).ToArray(); ChatMessage chatMessage = messages.FirstOrDefault((ChatMessage m) => m.Role == "system"); Dictionary dictionary = new Dictionary(); dictionary["contents"] = list; dictionary["tools"] = new[] { new { function_declarations } }; dictionary["generationConfig"] = new { temperature = _settings.Settings.Llm.Temperature, maxOutputTokens = _settings.Settings.Llm.MaxContextTokens }; Dictionary dictionary2 = dictionary; if (chatMessage != null) { dictionary2["systemInstruction"] = new { parts = new[] { new { text = chatMessage.Content } } }; } return dictionary2; } private async Task> SendOpenAiWithToolsAsync(List messages, IReadOnlyCollection tools, CancellationToken ct) { _ = _settings.Settings.Llm; string activeService = ResolveService(); object body = BuildOpenAiToolBody(messages, tools); string resolvedEp = ResolveServerInfo().Endpoint; string endpoint = (string.IsNullOrEmpty(resolvedEp) ? ResolveEndpointForService(activeService) : resolvedEp); string url = ((activeService.ToLowerInvariant() == "ollama") ? (endpoint.TrimEnd('/') + "/api/chat") : (endpoint.TrimEnd('/') + "/v1/chat/completions")); string json = JsonSerializer.Serialize(body); using HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; await ApplyAuthHeaderAsync(req, ct); using HttpResponseMessage resp = await _http.SendAsync(req, ct); if (!resp.IsSuccessStatusCode) { string errBody = await resp.Content.ReadAsStringAsync(ct); string detail = ExtractErrorDetail(errBody); LogService.Warn($"[ToolUse] {activeService} API 오류 ({resp.StatusCode}): {errBody}"); if (resp.StatusCode == HttpStatusCode.BadRequest) { throw new ToolCallNotSupportedException($"{activeService} API 오류 ({resp.StatusCode}): {detail}"); } throw new HttpRequestException($"{activeService} API 오류 ({resp.StatusCode}): {detail}"); } using JsonDocument doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync(ct)); JsonElement root = doc.RootElement; TryParseOpenAiUsage(root); List blocks = new List(); JsonElement message; if (root.TryGetProperty("message", out var ollamaMsg)) { message = ollamaMsg; } else { if (!root.TryGetProperty("choices", out var choices) || choices.GetArrayLength() <= 0) { return blocks; } message = (choices[0].TryGetProperty("message", out var choiceMsg) ? choiceMsg : default(JsonElement)); } if (message.TryGetProperty("content", out var content)) { string text = content.GetString(); if (!string.IsNullOrWhiteSpace(text)) { blocks.Add(new ContentBlock { Type = "text", Text = text }); } } if (message.TryGetProperty("tool_calls", out var toolCalls)) { foreach (JsonElement tc in toolCalls.EnumerateArray()) { if (!tc.TryGetProperty("function", out var func)) { continue; } JsonElement? parsedArgs = null; if (func.TryGetProperty("arguments", out var argsEl)) { if (argsEl.ValueKind == JsonValueKind.String) { try { using JsonDocument argsDoc = JsonDocument.Parse(argsEl.GetString() ?? "{}"); parsedArgs = argsDoc.RootElement.Clone(); } catch { parsedArgs = null; } } else if (argsEl.ValueKind == JsonValueKind.Object || argsEl.ValueKind == JsonValueKind.Array) { parsedArgs = argsEl.Clone(); } } blocks.Add(new ContentBlock { Type = "tool_use", ToolName = (func.TryGetProperty("name", out var fnm) ? (fnm.GetString() ?? "") : ""), ToolId = (tc.TryGetProperty("id", out var id) ? (id.GetString() ?? Guid.NewGuid().ToString("N").Substring(0, 12)) : Guid.NewGuid().ToString("N").Substring(0, 12)), ToolInput = parsedArgs }); func = default(JsonElement); argsEl = default(JsonElement); fnm = default(JsonElement); id = default(JsonElement); } } return blocks; } private object BuildOpenAiToolBody(List messages, IReadOnlyCollection tools) { LlmSettings llm = _settings.Settings.Llm; List list = new List(); foreach (ChatMessage message in messages) { if (message.Role == "user" && message.Content.StartsWith("{\"type\":\"tool_result\"")) { try { using JsonDocument jsonDocument = JsonDocument.Parse(message.Content); JsonElement rootElement = jsonDocument.RootElement; list.Add(new { role = "tool", tool_call_id = rootElement.GetProperty("tool_use_id").GetString(), content = rootElement.GetProperty("content").GetString() }); } catch { goto IL_00d3; } continue; } goto IL_00d3; IL_0292: if (message.Role == "user") { List? images = message.Images; if (images != null && images.Count > 0) { List list2 = new List(); foreach (ImageAttachment image in message.Images) { list2.Add(new { type = "image_url", image_url = new { url = "data:" + image.MimeType + ";base64," + image.Base64 } }); } list2.Add(new { type = "text", text = message.Content }); list.Add(new { role = message.Role, content = list2 }); continue; } } list.Add(new { role = message.Role, content = message.Content }); continue; IL_00d3: if (message.Role == "assistant" && message.Content.StartsWith("{\"_tool_use_blocks\"")) { try { using JsonDocument jsonDocument2 = JsonDocument.Parse(message.Content); JsonElement property = jsonDocument2.RootElement.GetProperty("_tool_use_blocks"); string text = ""; List list3 = new List(); foreach (JsonElement item in property.EnumerateArray()) { string text2 = item.GetProperty("type").GetString(); if (text2 == "text") { text = item.GetProperty("text").GetString() ?? ""; } else if (text2 == "tool_use") { JsonElement value; string arguments = (item.TryGetProperty("input", out value) ? value.GetRawText() : "{}"); list3.Add(new { id = (item.GetProperty("id").GetString() ?? ""), type = "function", function = new { name = (item.GetProperty("name").GetString() ?? ""), arguments = arguments } }); } } list.Add(new { role = "assistant", content = (string.IsNullOrEmpty(text) ? null : text), tool_calls = list3 }); } catch { goto IL_0292; } continue; } goto IL_0292; } var tools2 = tools.Select(delegate(IAgentTool t) { Dictionary dictionary = new Dictionary { ["type"] = "object", ["properties"] = t.Parameters.Properties.ToDictionary, string, object>((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => BuildPropertySchema(kv.Value, upperCaseType: false)) }; List required = t.Parameters.Required; if (required != null && required.Count > 0) { dictionary["required"] = t.Parameters.Required; } return new { type = "function", function = new { name = t.Name, description = t.Description, parameters = dictionary } }; }).ToArray(); string text3 = ResolveService(); string model = ResolveModel(); if (text3.Equals("ollama", StringComparison.OrdinalIgnoreCase)) { return new { model = model, messages = list, tools = tools2, stream = false, options = new { temperature = llm.Temperature } }; } return new { model = model, messages = list, tools = tools2, stream = false, temperature = llm.Temperature, max_tokens = llm.MaxContextTokens }; } private static object BuildPropertySchema(ToolProperty prop, bool upperCaseType) { string type = (upperCaseType ? prop.Type.ToUpperInvariant() : prop.Type); if (prop.Type.Equals("array", StringComparison.OrdinalIgnoreCase) && prop.Items != null) { return new { type = type, description = prop.Description, items = BuildPropertySchema(prop.Items, upperCaseType) }; } List list = prop.Enum; if (list != null && list.Count > 0) { return new { type = type, description = prop.Description, @enum = prop.Enum }; } return new { type = type, description = prop.Description }; } private static string ExtractErrorDetail(string errBody) { if (string.IsNullOrWhiteSpace(errBody)) { return "응답 없음"; } try { using JsonDocument jsonDocument = JsonDocument.Parse(errBody); if (jsonDocument.RootElement.TryGetProperty("error", out var value)) { if (value.ValueKind == JsonValueKind.String) { return value.GetString() ?? errBody; } if (value.ValueKind == JsonValueKind.Object && value.TryGetProperty("message", out var value2)) { return value2.GetString() ?? errBody; } } } catch { } return (errBody.Length > 500) ? (errBody.Substring(0, 500) + "…") : errBody; } }