namespace MiaoJiZhang.Api.Services; public interface ILlmClient { bool IsEnabled { get; } Task TryParseIntentAsync(string userText, CancellationToken ct = default); Task TryGenerateReplyAsync(string systemPrompt, string userText, CancellationToken ct = default); Task?> AnalyzeImageAsync( byte[] imageBytes, string mimeType, CancellationToken ct = default); Task?> ReconcileRecognitionBatchAsync( RecognitionBatchModelInput input, CancellationToken ct = default); Task<(bool Ok, string? Error)> TestConnectionAsync(CancellationToken ct = default); Task RunAgentAsync( string systemPrompt, string userText, IReadOnlyList tools, Func> executeTool, Func? onToken = null, Func? onToolRunning = null, CancellationToken ct = default); /// 流式生成回复。将 LLM 返回的 token 逐个写入 writer,完成时返回。 Task StreamReplyAsync( string systemPrompt, string userText, Func onToken, CancellationToken ct = default); } public record AgentToolDefinition( string Name, string Description, object Parameters); public record AgentToolCall( string CallId, string Name, string Arguments); public record AgentRunResponse( string Text, int ToolCallCount); public record ImageParseResult( string Type, decimal Amount, string CategoryName, string? PaymentMethod, string Note, DateTime? OccurredAt); public record RecognitionBatchModelCandidate( string CandidateId, string? FlowSessionId, string PackageName, string Type, decimal Amount, string? Merchant, string? OrderId, DateTime OccurredAt, string RecognitionKind, string? CategoryHint, string Confidence, IReadOnlyList EvidenceIds); public record RecognitionBatchModelEvidence( string EvidenceId, string? CandidateId, string? FlowSessionId, string PackageName, DateTime CapturedAt, byte[] ImageBytes, string MimeType); public record RecognitionBatchModelInput( string BatchId, IReadOnlyList Candidates, IReadOnlyList Evidence, IReadOnlyList ExpenseCategories, IReadOnlyList IncomeCategories); public record RecognitionBatchModelAction( string Action, string ActionId, string? CandidateId, string? EvidenceId, string? Type, decimal? Amount, string? CategoryName, string? PaymentMethod, string? Note, DateTime? OccurredAt, double Confidence, string Reason); public class NullLlmClient : ILlmClient { public bool IsEnabled => false; public Task TryParseIntentAsync( string userText, CancellationToken ct = default) => Task.FromResult(null); public Task TryGenerateReplyAsync( string systemPrompt, string userText, CancellationToken ct = default) => Task.FromResult(null); public Task?> AnalyzeImageAsync( byte[] imageBytes, string mimeType, CancellationToken ct = default) => Task.FromResult?>(null); public Task?> ReconcileRecognitionBatchAsync( RecognitionBatchModelInput input, CancellationToken ct = default) => Task.FromResult?>(null); public Task<(bool Ok, string? Error)> TestConnectionAsync( CancellationToken ct = default) => Task.FromResult<(bool, string?)>((false, "API Key 为空")); public Task RunAgentAsync( string systemPrompt, string userText, IReadOnlyList tools, Func> executeTool, Func? onToken = null, Func? onToolRunning = null, CancellationToken ct = default) => Task.FromResult(new AgentRunResponse("", 0)); public async Task StreamReplyAsync( string systemPrompt, string userText, Func onToken, CancellationToken ct = default) { await Task.CompletedTask; } }