Spaces:
Sleeping
Sleeping
| interface OpenAIMessage { | |
| role: 'system' | 'user' | 'assistant' | 'developer'; | |
| content: string | Array<{ | |
| type: 'text' | 'image_url'; | |
| text?: string; | |
| image_url?: { | |
| url: string; | |
| detail?: 'low' | 'high' | 'auto'; | |
| }; | |
| }>; | |
| } | |
| interface OpenAIResponse { | |
| text: string; | |
| durationMs: number; | |
| error?: string; | |
| } | |
| interface OpenAIStreamChunk { | |
| id: string; | |
| object: string; | |
| created: number; | |
| model: string; | |
| choices: Array<{ | |
| index: number; | |
| delta: { | |
| role?: string; | |
| content?: string; | |
| }; | |
| finish_reason?: string | null; | |
| }>; | |
| } | |
| const DEFAULT_OPENAI_API_BASE = 'https://api.openai.com/v1'; | |
| export const generateResponse = async ( | |
| prompt: string, | |
| modelName: string, | |
| systemInstruction?: string, | |
| shouldUseReducedCapacity: boolean = false, | |
| imagePart?: { inlineData: { mimeType: string; data: string } }, | |
| customBaseUrl?: string, | |
| apiKey?: string, | |
| onStreamChunk?: (newChunk: string, fullText: string, isComplete: boolean) => void, | |
| maxTokens?: number // 新增参数 | |
| ): Promise<OpenAIResponse> => { | |
| const startTime = performance.now(); | |
| try { | |
| if (!apiKey?.trim()) { | |
| throw new Error("API密钥未设置。请配置您的OpenAI API密钥。"); | |
| } | |
| const messages: OpenAIMessage[] = []; | |
| if (systemInstruction) { | |
| messages.push({ | |
| role: 'system', | |
| content: systemInstruction | |
| }); | |
| } | |
| if (imagePart) { | |
| const imageUrl = `data:${imagePart.inlineData.mimeType};base64,${imagePart.inlineData.data}`; | |
| messages.push({ | |
| role: 'user', | |
| content: [ | |
| { | |
| type: 'text', | |
| text: prompt | |
| }, | |
| { | |
| type: 'image_url', | |
| image_url: { | |
| url: imageUrl, | |
| detail: 'auto' | |
| } | |
| } | |
| ] | |
| }); | |
| } else { | |
| messages.push({ | |
| role: 'user', | |
| content: prompt | |
| }); | |
| } | |
| const requestBody = { | |
| model: modelName, | |
| messages: messages, | |
| stream: !!onStreamChunk, | |
| temperature: shouldUseReducedCapacity ? 0.3 : 0.7, | |
| max_tokens: shouldUseReducedCapacity | |
| ? Math.min(1000, maxTokens || 4000) // 快速模式下使用较小值 | |
| : (maxTokens || 4000) // 使用传入的 maxTokens 或默认值 | |
| }; | |
| const apiBase = customBaseUrl || DEFAULT_OPENAI_API_BASE; | |
| const response = await fetch(`${apiBase}/chat/completions`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${apiKey}` | |
| }, | |
| body: JSON.stringify(requestBody) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json().catch(() => ({})); | |
| const errorMessage = errorData.error?.message || response.statusText; | |
| if (response.status === 401) { | |
| throw new Error(`API密钥无效或已过期: ${errorMessage}`); | |
| } else if (response.status === 429) { | |
| throw new Error(`API调用频率超限: ${errorMessage}`); | |
| } else if (response.status === 404) { | |
| throw new Error(`模型不存在或无权访问: ${modelName}`); | |
| } else { | |
| throw new Error(`OpenAI API 错误 (${response.status}): ${errorMessage}`); | |
| } | |
| } | |
| let fullText = ''; | |
| const durationMs = performance.now() - startTime; | |
| // 处理流式响应 | |
| if (onStreamChunk && requestBody.stream) { | |
| const reader = response.body?.getReader(); | |
| const decoder = new TextDecoder(); | |
| if (!reader) { | |
| throw new Error('无法读取响应流'); | |
| } | |
| try { | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value); | |
| const lines = chunk.split('\n'); | |
| for (const line of lines) { | |
| const trimmed = line.trim(); | |
| if (trimmed.startsWith('data: ')) { | |
| const jsonStr = trimmed.slice(6); | |
| if (jsonStr === '[DONE]') { | |
| onStreamChunk('', fullText, true); | |
| break; | |
| } | |
| try { | |
| const parsed: OpenAIStreamChunk = JSON.parse(jsonStr); | |
| const content = parsed.choices[0]?.delta?.content; | |
| if (content) { | |
| fullText += content; | |
| // 立即回调以实现实时显示 | |
| onStreamChunk(content, fullText, false); | |
| } | |
| } catch (parseError) { | |
| console.warn('解析流数据时出错:', parseError); | |
| } | |
| } | |
| } | |
| } | |
| } finally { | |
| reader.releaseLock(); | |
| } | |
| } else { | |
| // 处理非流式响应(例如图像请求) | |
| const data = await response.json(); | |
| fullText = data.choices[0]?.message?.content || ''; | |
| } | |
| if (!fullText.trim()) { | |
| throw new Error('AI响应为空,请检查模型配置或重试'); | |
| } | |
| return { text: fullText, durationMs }; | |
| } catch (error) { | |
| console.error("调用OpenAI API时出错:", error); | |
| const durationMs = performance.now() - startTime; | |
| if (error instanceof Error) { | |
| if (error.message.includes('API密钥') || error.message.includes('401') || error.message.includes('Unauthorized')) { | |
| return { text: "API密钥无效或已过期。请检查您的OpenAI API密钥配置。", durationMs, error: "API key not valid" }; | |
| } else if (error.message.includes('429') || error.message.includes('Rate limit')) { | |
| return { text: "API调用频率超限,请稍后重试。", durationMs, error: "Rate limit exceeded" }; | |
| } else if (error.message.includes('404') || error.message.includes('model')) { | |
| return { text: `模型 ${modelName} 不存在或无权访问。请检查模型名称或API权限。`, durationMs, error: "Model not found" }; | |
| } else if (error.message.includes('网络') || error.message.includes('fetch')) { | |
| return { text: "网络连接错误,请检查网络连接后重试。", durationMs, error: "Network error" }; | |
| } | |
| return { text: `与OpenAI通信时出错: ${error.message}`, durationMs, error: error.message }; | |
| } | |
| return { text: "与OpenAI通信时发生未知错误。", durationMs, error: "Unknown OpenAI error" }; | |
| } | |
| }; |