import OpenAI from 'openai'; import type { AiProvider, PingRequest, PingResponse, ToolCallRequest, ToolCallResponse } from './provider.interface'; /** * OpenAI 格式提供商(兼容 OpenAI Chat Completions 风格的中转站) */ export class OpenAIProvider implements AiProvider { readonly format = 'openai' as const; private client: OpenAI; constructor(apiKey: string, baseURL: string) { this.client = new OpenAI({ apiKey, baseURL, }); } async callTool(req: ToolCallRequest): Promise { const response = await this.client.chat.completions.create({ model: req.model, max_tokens: req.maxTokens, messages: [ { role: 'system', content: req.systemPrompt }, { role: 'user', content: req.userPrompt }, ], tools: [ { type: 'function', function: { name: req.tool.name, description: req.tool.description, parameters: req.tool.inputSchema, }, }, ], tool_choice: req.forceTool ? { type: 'function', function: { name: req.tool.name } } : 'auto', }); const choice = response.choices[0]; if (!choice?.message?.tool_calls?.length) { throw new Error('OpenAI 未通过 tool_calls 返回结果'); } const tc = choice.message.tool_calls[0]; if (tc.type !== 'function') { throw new Error('OpenAI 返回的 tool_call 类型非 function'); } let parsed: Record; try { parsed = JSON.parse(tc.function.arguments || '{}'); } catch (e) { throw new Error(`OpenAI tool_call arguments 不是合法 JSON: ${(e as Error).message}`); } return { toolName: tc.function.name, toolInput: parsed, inputTokens: response.usage?.prompt_tokens ?? 0, outputTokens: response.usage?.completion_tokens ?? 0, rawModel: response.model, }; } async ping(req: PingRequest): Promise { const resp = await this.client.chat.completions.create({ model: req.model, max_tokens: 16, messages: [{ role: 'user', content: 'ping' }], }); return { ok: true, inputTokens: resp.usage?.prompt_tokens ?? 0, outputTokens: resp.usage?.completion_tokens ?? 0, }; } }