feat(平台): 补齐服务端持久化和AI拆解契约
This commit is contained in:
79
apps/server/src/modules/ai/providers/openai.provider.ts
Normal file
79
apps/server/src/modules/ai/providers/openai.provider.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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<ToolCallResponse> {
|
||||
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<string, any>;
|
||||
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<PingResponse> {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user