UNPKG

openai-cli-unofficial

Version:

A powerful OpenAI CLI Coding Agent built with TypeScript

203 lines 8.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.openAIService = void 0; const openai_1 = __importDefault(require("openai")); const storage_1 = require("./storage"); class OpenAIService { constructor() { this.MAX_RETRIES = 3; this.RETRY_DELAY_MS = 1000; this.openai = null; // Initialize to null this.updateConfig(); // 监听配置变更事件 storage_1.StorageService.onConfigChange(() => { this.updateConfig(); }); } updateConfig() { const apiConfig = storage_1.StorageService.getApiConfig(); if (apiConfig.apiKey && apiConfig.apiKey.trim() !== '') { this.openai = new openai_1.default({ apiKey: apiConfig.apiKey, baseURL: apiConfig.baseUrl, }); } else { this.openai = null; } } async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async streamChat(options) { if (!this.openai) { const error = new Error("OpenAI API key is not configured. Please run 'openai-cli' and go to 'Configuration' to set it up."); options.onError(error); return { status: 'done', assistantResponse: { content: null } }; } const apiConfig = storage_1.StorageService.getApiConfig(); for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) { try { const result = await this.performStreamChat(options, apiConfig); // 检查AI回复是否为空(算作失败) const hasContent = result.assistantResponse.content && result.assistantResponse.content.trim() !== ''; const hasToolCalls = result.assistantResponse.tool_calls && result.assistantResponse.tool_calls.length > 0; if (!hasContent && !hasToolCalls) { throw new Error('AI response is empty'); } return result; } catch (error) { const isLastAttempt = attempt === this.MAX_RETRIES; if (isLastAttempt) { options.onError(error); return { status: 'done', assistantResponse: { content: null } }; } // 等待后重试 await this.delay(this.RETRY_DELAY_MS * attempt); console.warn(`OpenAI API request failed (attempt ${attempt}/${this.MAX_RETRIES}):`, error.message); } } // 这里不应该到达,但为了类型安全 const finalError = new Error('All retry attempts failed'); options.onError(finalError); return { status: 'done', assistantResponse: { content: null } }; } async performStreamChat(options, apiConfig) { // 打印请消息 // console.log('--- OpenAI Request Body [DEBUG] ---'); // console.log(JSON.stringify(options.messages, null, 2)); // console.log('------------------------------------'); // 打印系统提示词 // console.log('--- System Prompt [DEBUG] ---'); // console.log(options.messages[0].content); // console.log('------------------------------------'); const stream = await this.openai.chat.completions.create({ model: apiConfig.model || 'gpt-4.1', messages: options.messages, stream: true, tools: options.tools, tool_choice: options.tools ? 'auto' : undefined, }); let accumulatedContent = ''; let toolCalls = []; let currentToolCallId = null; let currentToolCallFunction = { name: '', arguments: '' }; let reasoningContent = ''; for await (const chunk of stream) { const delta = chunk.choices[0]?.delta; if (delta?.content) { accumulatedContent += delta.content; options.onChunk(delta.content); } const reasoningChunk = delta?.reasoning_content || delta?.reasoning; if (reasoningChunk) { reasoningContent += reasoningChunk; options.onReasoningChunk(reasoningChunk); } if (delta?.tool_calls) { for (const toolCall of delta.tool_calls) { if (toolCall.id) { if (currentToolCallId) { // A new tool call has started, so we finalize the previous one toolCalls.push({ id: currentToolCallId, type: 'function', function: { name: currentToolCallFunction.name, arguments: currentToolCallFunction.arguments, }, }); } currentToolCallId = toolCall.id; currentToolCallFunction = { name: '', arguments: '' }; } if (toolCall.function?.name) { currentToolCallFunction.name += toolCall.function.name; } if (toolCall.function?.arguments) { currentToolCallFunction.arguments += toolCall.function.arguments; } } } const finishReason = chunk.choices[0]?.finish_reason; if (finishReason === 'tool_calls' || (finishReason === 'stop' && currentToolCallId)) { if (currentToolCallId) { toolCalls.push({ id: currentToolCallId, type: 'function', function: { name: currentToolCallFunction.name, arguments: currentToolCallFunction.arguments, }, }); } if (options.onAssistantMessage) { options.onAssistantMessage({ content: accumulatedContent, toolCalls }); } return { status: 'tool_calls', assistantResponse: { content: accumulatedContent || null, tool_calls: toolCalls } }; } } if (options.onComplete) { options.onComplete(accumulatedContent); } return { status: 'done', assistantResponse: { content: accumulatedContent } }; } async chat(options) { if (!this.openai) { throw new Error("OpenAI API key is not configured. Please run 'openai-cli' and go to 'Configuration' to set it up."); } for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) { try { const result = await this.performChat(options); // 检查AI回复是否为空(算作失败) if (!result || result.trim() === '') { throw new Error('AI response is empty'); } return result; } catch (error) { const isLastAttempt = attempt === this.MAX_RETRIES; if (isLastAttempt) { throw error; } // 等待后重试 await this.delay(this.RETRY_DELAY_MS * attempt); console.warn(`OpenAI API request failed (attempt ${attempt}/${this.MAX_RETRIES}):`, error.message); } } // 这里不应该到达,但为了类型安全 throw new Error('All retry attempts failed'); } async performChat(options) { const apiConfig = storage_1.StorageService.getApiConfig(); const response = await this.openai.chat.completions.create({ model: apiConfig.model || 'gpt-4.1', messages: options.messages, response_format: options.responseFormat ? { type: options.responseFormat } : undefined, temperature: options.temperature, max_tokens: options.maxTokens, }); return response.choices[0]?.message?.content || ''; } } exports.openAIService = new OpenAIService(); //# sourceMappingURL=openai.js.map