Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Services/LlmService.cs

4464 lines
126 KiB
C#

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<string>, IAsyncEnumerator<string>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine
{
public int _003C_003E1__state;
public AsyncIteratorMethodBuilder _003C_003Et__builder;
public ManualResetValueTaskSourceCore<bool> _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<ChatMessage> messages;
public List<ChatMessage> _003C_003E3__messages;
private CancellationToken ct;
public CancellationToken _003C_003E3__ct;
public LlmService _003C_003E4__this;
private string _003CactiveService_003E5__1;
private IAsyncEnumerable<string> _003Cstream_003E5__2;
private string _003C_003Es__3;
private ConfiguredCancelableAsyncEnumerable<string>.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<bool>.ConfiguredValueTaskAwaiter _003C_003Eu__1;
private ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter _003C_003Eu__2;
string IAsyncEnumerator<string>.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<string> 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<bool>.ConfiguredValueTaskAwaiter awaiter2;
if (num != -4)
{
if (num != 0)
{
goto IL_01aa;
}
awaiter2 = _003C_003Eu__1;
_003C_003Eu__1 = default(ConfiguredValueTaskAwaitable<bool>.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<string>.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<string>.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<string>.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<string> IAsyncEnumerable<string>.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<bool> IAsyncEnumerator<string>.MoveNextAsync()
{
if (_003C_003E1__state == -2)
{
return default(ValueTask<bool>);
}
_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<bool>(_003C_003Ev__promiseOfValueOrEnd.GetResult(version));
}
return new ValueTask<bool>(this, version);
}
[DebuggerHidden]
bool IValueTaskSource<bool>.GetResult(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetResult(token);
}
[DebuggerHidden]
ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token);
}
[DebuggerHidden]
void IValueTaskSource<bool>.OnCompleted(Action<object?> 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<object?> 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<string>, IAsyncEnumerator<string>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine
{
public int _003C_003E1__state;
public AsyncIteratorMethodBuilder _003C_003Et__builder;
public ManualResetValueTaskSourceCore<bool> _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<ChatMessage> messages;
public List<ChatMessage> _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<HttpResponseMessage> _003C_003Eu__1;
private TaskAwaiter<string> _003C_003Eu__2;
private TaskAwaiter<Stream> _003C_003Eu__3;
string IAsyncEnumerator<string>.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<HttpResponseMessage> 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<HttpResponseMessage>);
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<string> awaiter3;
TaskAwaiter<Stream> 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<string>);
num = (_003C_003E1__state = -1);
goto IL_02ad;
case 2:
awaiter2 = _003C_003Eu__3;
_003C_003Eu__3 = default(TaskAwaiter<Stream>);
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<string> awaiter4;
if (num != -4)
{
if (num != 3)
{
goto IL_071e;
}
awaiter4 = _003C_003Eu__2;
_003C_003Eu__2 = default(TaskAwaiter<string>);
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<string> IAsyncEnumerable<string>.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<bool> IAsyncEnumerator<string>.MoveNextAsync()
{
if (_003C_003E1__state == -2)
{
return default(ValueTask<bool>);
}
_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<bool>(_003C_003Ev__promiseOfValueOrEnd.GetResult(version));
}
return new ValueTask<bool>(this, version);
}
[DebuggerHidden]
bool IValueTaskSource<bool>.GetResult(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetResult(token);
}
[DebuggerHidden]
ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token);
}
[DebuggerHidden]
void IValueTaskSource<bool>.OnCompleted(Action<object?> 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<object?> 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<string>, IAsyncEnumerator<string>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine
{
public int _003C_003E1__state;
public AsyncIteratorMethodBuilder _003C_003Et__builder;
public ManualResetValueTaskSourceCore<bool> _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<ChatMessage> messages;
public List<ChatMessage> _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<HttpResponseMessage> _003C_003Eu__1;
private TaskAwaiter<Stream> _003C_003Eu__2;
private TaskAwaiter<string?> _003C_003Eu__3;
string IAsyncEnumerator<string>.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<HttpResponseMessage> 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<HttpResponseMessage>);
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<Stream> 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<Stream>);
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<string> awaiter3;
if (num != -4)
{
if (num != 2)
{
goto IL_062f;
}
awaiter3 = _003C_003Eu__3;
_003C_003Eu__3 = default(TaskAwaiter<string>);
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<string> IAsyncEnumerable<string>.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<bool> IAsyncEnumerator<string>.MoveNextAsync()
{
if (_003C_003E1__state == -2)
{
return default(ValueTask<bool>);
}
_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<bool>(_003C_003Ev__promiseOfValueOrEnd.GetResult(version));
}
return new ValueTask<bool>(this, version);
}
[DebuggerHidden]
bool IValueTaskSource<bool>.GetResult(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetResult(token);
}
[DebuggerHidden]
ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token);
}
[DebuggerHidden]
void IValueTaskSource<bool>.OnCompleted(Action<object?> 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<object?> 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<string>, IAsyncEnumerator<string>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine
{
public int _003C_003E1__state;
public AsyncIteratorMethodBuilder _003C_003Et__builder;
public ManualResetValueTaskSourceCore<bool> _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<ChatMessage> messages;
public List<ChatMessage> _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<HttpResponseMessage> _003C_003Eu__1;
private TaskAwaiter<Stream> _003C_003Eu__2;
private TaskAwaiter<string?> _003C_003Eu__3;
string IAsyncEnumerator<string>.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<HttpResponseMessage> 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<HttpResponseMessage>);
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<Stream> 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<Stream>);
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<string> awaiter3;
if (num != -4)
{
if (num != 2)
{
goto IL_04f1;
}
awaiter3 = _003C_003Eu__3;
_003C_003Eu__3 = default(TaskAwaiter<string>);
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<string> IAsyncEnumerable<string>.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<bool> IAsyncEnumerator<string>.MoveNextAsync()
{
if (_003C_003E1__state == -2)
{
return default(ValueTask<bool>);
}
_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<bool>(_003C_003Ev__promiseOfValueOrEnd.GetResult(version));
}
return new ValueTask<bool>(this, version);
}
[DebuggerHidden]
bool IValueTaskSource<bool>.GetResult(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetResult(token);
}
[DebuggerHidden]
ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token);
}
[DebuggerHidden]
void IValueTaskSource<bool>.OnCompleted(Action<object?> 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<object?> 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<string>, IAsyncEnumerator<string>, IAsyncDisposable, IValueTaskSource<bool>, IValueTaskSource, IAsyncStateMachine
{
public int _003C_003E1__state;
public AsyncIteratorMethodBuilder _003C_003Et__builder;
public ManualResetValueTaskSourceCore<bool> _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<ChatMessage> messages;
public List<ChatMessage> _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<HttpResponseMessage> _003C_003Eu__2;
private TaskAwaiter<Stream> _003C_003Eu__3;
private TaskAwaiter<string?> _003C_003Eu__4;
string IAsyncEnumerator<string>.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<HttpResponseMessage> 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<HttpResponseMessage>);
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<Stream> 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<Stream>);
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<string> awaiter4;
if (num != -4)
{
if (num != 3)
{
goto IL_05dc;
}
awaiter4 = _003C_003Eu__4;
_003C_003Eu__4 = default(TaskAwaiter<string>);
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<string> IAsyncEnumerable<string>.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<bool> IAsyncEnumerator<string>.MoveNextAsync()
{
if (_003C_003E1__state == -2)
{
return default(ValueTask<bool>);
}
_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<bool>(_003C_003Ev__promiseOfValueOrEnd.GetResult(version));
}
return new ValueTask<bool>(this, version);
}
[DebuggerHidden]
bool IValueTaskSource<bool>.GetResult(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetResult(token);
}
[DebuggerHidden]
ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)
{
return _003C_003Ev__promiseOfValueOrEnd.GetStatus(token);
}
[DebuggerHidden]
void IValueTaskSource<bool>.OnCompleted(Action<object?> 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<object?> 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<string?> 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<string> SendAsync(List<ChatMessage> 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<string> SendWithServiceAsync(string service, List<ChatMessage> messages, CancellationToken ct)
{
string text = service.ToLowerInvariant();
if (1 == 0)
{
}
Task<string> 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<string> StreamAsync(List<ChatMessage> 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<string> SendOllamaAsync(List<ChatMessage> 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<string> StreamOllamaAsync(List<ChatMessage> 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<ChatMessage> messages, bool stream)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> messages2 = BuildMessageList(messages);
return new
{
model = ResolveModelName(),
messages = messages2,
stream = stream,
options = new
{
temperature = llm.Temperature
}
};
}
private async Task<string> SendOpenAiCompatibleAsync(List<ChatMessage> 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<string> StreamOpenAiCompatibleAsync(List<ChatMessage> 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<ChatMessage> messages, bool stream)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> messages2 = BuildMessageList(messages, openAiVision: true);
return new
{
model = ResolveModelName(),
messages = messages2,
stream = stream,
temperature = llm.Temperature,
max_tokens = llm.MaxContextTokens
};
}
private async Task<string> SendGeminiAsync(List<ChatMessage> 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<string> StreamGeminiAsync(List<ChatMessage> 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<ChatMessage> messages)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> list = new List<object>();
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<object> list2 = new List<object>
{
new
{
text = message.Content
}
};
List<ImageAttachment>? 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<string> SendClaudeAsync(List<ChatMessage> 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<string> StreamClaudeAsync(List<ChatMessage> 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<ChatMessage> messages, bool stream)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> list = new List<object>();
foreach (ChatMessage message in messages)
{
if (message.Role == "system")
{
continue;
}
List<ImageAttachment>? images = message.Images;
if (images != null && images.Count > 0)
{
List<object> list2 = new List<object>();
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<object> BuildMessageList(List<ChatMessage> messages, bool openAiVision = false)
{
List<object> list = new List<object>();
if (!string.IsNullOrEmpty(_systemPrompt))
{
list.Add(new
{
role = "system",
content = _systemPrompt
});
}
foreach (ChatMessage message in messages)
{
if (message.Role == "system")
{
continue;
}
List<ImageAttachment>? images = message.Images;
if (images != null && images.Count > 0)
{
if (openAiVision)
{
List<object> list2 = new List<object>();
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<string> 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<HttpResponseMessage> 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<string?> 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<JsonElement, string> 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<List<ContentBlock>> SendWithToolsAsync(List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools, CancellationToken ct = default(CancellationToken))
{
string activeService = ResolveService();
string text = activeService.ToLowerInvariant();
if (1 == 0)
{
}
List<ContentBlock> 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<List<ContentBlock>> SendClaudeWithToolsAsync(List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> 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<ContentBlock> blocks = new List<ContentBlock>();
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<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> list = new List<object>();
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<object> list2 = new List<object>();
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<ImageAttachment>? images = message.Images;
if (images != null && images.Count > 0 && message.Role == "user")
{
List<object> list3 = new List<object>();
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<KeyValuePair<string, ToolProperty>, string, object>((KeyValuePair<string, ToolProperty> kv) => kv.Key, (KeyValuePair<string, ToolProperty> 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<List<ContentBlock>> SendGeminiWithToolsAsync(List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> 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<ContentBlock> blocks = new List<ContentBlock>();
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<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools)
{
List<object> list = new List<object>();
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<object> list2 = new List<object>();
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<ImageAttachment>? images = message.Images;
if (images != null && images.Count > 0 && message.Role == "user")
{
List<object> list3 = new List<object>
{
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<KeyValuePair<string, ToolProperty>, string, object>((KeyValuePair<string, ToolProperty> kv) => kv.Key, (KeyValuePair<string, ToolProperty> kv) => BuildPropertySchema(kv.Value, upperCaseType: true)),
required = t.Parameters.Required
}
}).ToArray();
ChatMessage chatMessage = messages.FirstOrDefault((ChatMessage m) => m.Role == "system");
Dictionary<string, object> dictionary = new Dictionary<string, object>();
dictionary["contents"] = list;
dictionary["tools"] = new[]
{
new { function_declarations }
};
dictionary["generationConfig"] = new
{
temperature = _settings.Settings.Llm.Temperature,
maxOutputTokens = _settings.Settings.Llm.MaxContextTokens
};
Dictionary<string, object> dictionary2 = dictionary;
if (chatMessage != null)
{
dictionary2["systemInstruction"] = new
{
parts = new[]
{
new
{
text = chatMessage.Content
}
}
};
}
return dictionary2;
}
private async Task<List<ContentBlock>> SendOpenAiWithToolsAsync(List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> 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<ContentBlock> blocks = new List<ContentBlock>();
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<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools)
{
LlmSettings llm = _settings.Settings.Llm;
List<object> list = new List<object>();
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<ImageAttachment>? images = message.Images;
if (images != null && images.Count > 0)
{
List<object> list2 = new List<object>();
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<object> list3 = new List<object>();
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<string, object> dictionary = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = t.Parameters.Properties.ToDictionary<KeyValuePair<string, ToolProperty>, string, object>((KeyValuePair<string, ToolProperty> kv) => kv.Key, (KeyValuePair<string, ToolProperty> kv) => BuildPropertySchema(kv.Value, upperCaseType: false))
};
List<string> 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<string> 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;
}
}