- AX Agent 스트리밍 경과 시간을 공용 helper로 통일해 비정상적인 수천만 시간 표시를 방지함 - 채팅 입력창 글로우를 런처와 같은 표시/숨김 중심의 얇은 외곽 글로우로 정리하고 런처 글로우 설정은 일반 설정에 유지함 - README와 DEVELOPMENT 문서를 2026-04-08 12:02 (KST) 기준으로 갱신하고 Release 빌드 경고 0 / 오류 0을 확인함
116 lines
3.2 KiB
C#
116 lines
3.2 KiB
C#
using System.Net.Http;
|
|
using AxCopilot.Models;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
internal sealed class ServerStatusService
|
|
{
|
|
public static readonly ServerStatusService Instance = new();
|
|
|
|
private static readonly HttpClient Http = new()
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(1500)
|
|
};
|
|
|
|
public bool OllamaOnline { get; private set; }
|
|
public bool LlmOnline { get; private set; }
|
|
public bool McpOnline { get; private set; }
|
|
public string McpName { get; private set; } = "MCP";
|
|
|
|
public event EventHandler? StatusChanged;
|
|
|
|
private System.Threading.Timer? _timer;
|
|
private string _ollamaEndpoint = "http://localhost:11434";
|
|
private string _llmEndpoint = "";
|
|
private string _llmService = "Ollama";
|
|
private string _mcpEndpoint = "";
|
|
|
|
private ServerStatusService() { }
|
|
|
|
public void Start(AppSettings? settings = null)
|
|
{
|
|
LoadEndpoints(settings);
|
|
if (_timer != null)
|
|
return;
|
|
|
|
_timer = new System.Threading.Timer(async _ => await CheckAllAsync(), null, 0, 60000);
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_timer?.Dispose();
|
|
_timer = null;
|
|
}
|
|
|
|
public void Refresh(AppSettings? settings = null)
|
|
{
|
|
LoadEndpoints(settings);
|
|
_ = CheckAllAsync();
|
|
}
|
|
|
|
private void LoadEndpoints(AppSettings? settings)
|
|
{
|
|
var llm = settings?.Llm;
|
|
if (llm == null)
|
|
return;
|
|
|
|
_ollamaEndpoint = llm.OllamaEndpoint?.TrimEnd('/') ?? "http://localhost:11434";
|
|
_llmService = llm.Service ?? "Ollama";
|
|
_llmEndpoint = string.Equals(_llmService, "vLLM", StringComparison.OrdinalIgnoreCase)
|
|
? (llm.VllmEndpoint?.TrimEnd('/') ?? "")
|
|
: _ollamaEndpoint;
|
|
|
|
var mcp = llm.McpServers?.FirstOrDefault(s => s.Enabled && !string.IsNullOrWhiteSpace(s.Url));
|
|
if (mcp != null)
|
|
{
|
|
McpName = mcp.Name;
|
|
_mcpEndpoint = mcp.Url?.TrimEnd('/') ?? "";
|
|
}
|
|
else
|
|
{
|
|
McpName = "MCP";
|
|
_mcpEndpoint = "";
|
|
}
|
|
}
|
|
|
|
private async Task CheckAllAsync()
|
|
{
|
|
var ollamaTask = PingAsync(_ollamaEndpoint + "/api/version");
|
|
var llmTask = string.IsNullOrEmpty(_llmEndpoint) || _llmEndpoint == _ollamaEndpoint
|
|
? ollamaTask
|
|
: PingAsync(_llmEndpoint);
|
|
var mcpTask = string.IsNullOrEmpty(_mcpEndpoint)
|
|
? Task.FromResult(false)
|
|
: PingAsync(_mcpEndpoint);
|
|
|
|
await Task.WhenAll(ollamaTask, llmTask, mcpTask).ConfigureAwait(false);
|
|
|
|
var changed = OllamaOnline != ollamaTask.Result ||
|
|
LlmOnline != llmTask.Result ||
|
|
McpOnline != mcpTask.Result;
|
|
|
|
OllamaOnline = ollamaTask.Result;
|
|
LlmOnline = llmTask.Result;
|
|
McpOnline = mcpTask.Result;
|
|
|
|
if (changed)
|
|
StatusChanged?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private static async Task<bool> PingAsync(string url)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(url))
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var resp = await Http.GetAsync(url).ConfigureAwait(false);
|
|
return resp.IsSuccessStatusCode || (int)resp.StatusCode < 500;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|