import OpenAI from "openai"; import { ModelOption } from '../../types'; import { withRetry } from '../utils/retry'; export interface OpenAIStreamChunk { text: string; thought?: string; } export interface OpenAIConfig { model: ModelOption; systemInstruction?: string; content: string | Array; temperature?: number; responseFormat?: 'text' | 'json_object'; thinkingConfig?: { includeThoughts: boolean; thinkingBudget: number; }; } const parseThinkingTokens = (text: string): { thought: string; text: string } => { const thinkPattern = /([\s\S]*?)<\/thinking>/g; let thought = ''; let cleanText = text; const matches = text.matchAll(thinkPattern); for (const match of matches) { thought += match[1]; } cleanText = text.replace(thinkPattern, ''); return { thought: thought.trim(), text: cleanText.trim() }; }; export const generateContent = async ( ai: OpenAI, config: OpenAIConfig ): Promise<{ text: string; thought?: string }> => { const messages: Array = []; if (config.systemInstruction) { messages.push({ role: 'system', content: config.systemInstruction }); } messages.push({ role: 'user', content: config.content as any }); const requestOptions: any = { model: config.model, messages, temperature: config.temperature, }; if (config.responseFormat === 'json_object') { requestOptions.response_format = { type: 'json_object' }; } try { const response = await withRetry(() => ai.chat.completions.create(requestOptions)); const message = response.choices[0]?.message; const content = message?.content || ''; // Check for DeepSeek native reasoning field const reasoningContent = (message as any)?.reasoning_content; if (reasoningContent && config.thinkingConfig?.includeThoughts) { return { text: content, thought: reasoningContent }; } if (config.thinkingConfig?.includeThoughts) { const { thought, text } = parseThinkingTokens(content); return { text, thought }; } return { text: content }; } catch (error) { console.error('OpenAI generateContent error:', error); throw error; } }; export async function* generateContentStream( ai: OpenAI, config: OpenAIConfig ): AsyncGenerator { const messages: Array = []; if (config.systemInstruction) { messages.push({ role: 'system', content: config.systemInstruction }); } messages.push({ role: 'user', content: config.content as any }); const requestOptions: any = { model: config.model, messages, temperature: config.temperature, stream: true, }; const stream = await withRetry(() => ai.chat.completions.create(requestOptions) as any); let accumulatedText = ''; let inThinking = false; let currentThought = ''; for await (const chunk of (stream as any)) { const delta = chunk.choices[0]?.delta?.content || ''; // Support DeepSeek native reasoning field const reasoningDelta = (chunk.choices[0]?.delta as any)?.reasoning_content || ''; // If we have native reasoning content, yield it immediately as thought if (reasoningDelta) { yield { text: '', thought: reasoningDelta }; } if (!delta) continue; accumulatedText += delta; if (config.thinkingConfig?.includeThoughts) { if (delta.includes('')) { inThinking = true; continue; } if (inThinking) { if (delta.includes('')) { inThinking = false; const parts = delta.split('', 2); currentThought += parts[0]; if (currentThought.trim()) { yield { text: '', thought: currentThought }; currentThought = ''; } if (parts[1]) { yield { text: parts[1], thought: '' }; } } else { currentThought += delta; if (currentThought.length > 100) { yield { text: '', thought: currentThought }; currentThought = ''; } } } else { yield { text: delta, thought: '' }; } } else { yield { text: delta, thought: '' }; } } if (currentThought.trim()) { yield { text: '', thought: currentThought }; } }