package llm import ( "context" "io" "github.com/yourname/cyrene-ai/ai-core/internal/model" ) // Adapter LLM适配器接口 // 支持不同的LLM后端(OpenAI、Ollama、vLLM等) type Adapter struct { provider LLMProvider } // OpenAITool 暴露给调用方使用的工具定义(与 openai.go 的 openAITool 等价) type OpenAITool struct { Type string `json:"type"` Function OpenAIToolFunc `json:"function"` } // OpenAIToolFunc 工具函数定义 type OpenAIToolFunc struct { Name string `json:"name"` Description string `json:"description"` Parameters map[string]interface{} `json:"parameters"` } // LLMProvider LLM提供商接口 type LLMProvider interface { // Chat 同步对话 Chat(ctx context.Context, messages []model.LLMMessage) (*model.LLMResponse, error) // ChatStream 流式对话,返回一个channel逐token推送 ChatStream(ctx context.Context, messages []model.LLMMessage) (<-chan StreamChunk, error) // ChatWithTools 同步对话(支持工具调用),tools 为 nil 时等价于 Chat ChatWithTools(ctx context.Context, messages []model.LLMMessage, tools []OpenAITool) (*model.LLMResponse, error) // ChatStreamWithTools 流式对话(支持工具调用),tools 为 nil 时等价于 ChatStream ChatStreamWithTools(ctx context.Context, messages []model.LLMMessage, tools []OpenAITool) (<-chan StreamChunk, error) // ModelName 返回当前使用的模型名称 ModelName() string } // StreamChunk 流式响应的单个片段 type StreamChunk struct { Content string // delta内容 Done bool // 是否为最后一块 Error error // 错误信息 Usage *model.Usage // 最后一块时返回token统计 } // NewAdapter 创建LLM适配器 func NewAdapter(provider LLMProvider) *Adapter { return &Adapter{provider: provider} } // Chat 同步对话 func (a *Adapter) Chat(ctx context.Context, messages []model.LLMMessage) (*model.LLMResponse, error) { return a.provider.Chat(ctx, messages) } // ChatWithTools 同步对话(支持工具调用) func (a *Adapter) ChatWithTools(ctx context.Context, messages []model.LLMMessage, tools []OpenAITool) (*model.LLMResponse, error) { return a.provider.ChatWithTools(ctx, messages, tools) } // ChatStream 流式对话 func (a *Adapter) ChatStream(ctx context.Context, messages []model.LLMMessage) (<-chan StreamChunk, error) { return a.provider.ChatStream(ctx, messages) } // ChatStreamWithTools 流式对话(支持工具调用) func (a *Adapter) ChatStreamWithTools(ctx context.Context, messages []model.LLMMessage, tools []OpenAITool) (<-chan StreamChunk, error) { return a.provider.ChatStreamWithTools(ctx, messages, tools) } // ModelName 返回模型名称 func (a *Adapter) ModelName() string { return a.provider.ModelName() } // collectStream 辅助函数:将流式响应收集为完整响应 func collectStream(ch <-chan StreamChunk) (*model.LLMResponse, error) { var content string var lastUsage *model.Usage for chunk := range ch { if chunk.Error != nil { return nil, chunk.Error } if chunk.Done { lastUsage = chunk.Usage break } content += chunk.Content } resp := &model.LLMResponse{ Content: content, FinishReason: "stop", } if lastUsage != nil { resp.Usage = *lastUsage } return resp, nil } // Ensure io is used (will be needed for SSE parsing) var _ io.Reader