const modelsCompletionsEndpoint = 'https://models.github.ai/inference/chat/completions' interface ChatMessage { role: string content: string } interface ChatCompletionRequest { messages: ChatMessage[] model?: string temperature?: number max_tokens?: number } interface ChatCompletionChoice { message: { content: string role: string } finish_reason: string index: number } interface ChatCompletionResponse { choices: ChatCompletionChoice[] id: string object: string created: number model: string usage?: { prompt_tokens: number completion_tokens: number total_tokens: number } } export async function callModelsApi( promptWithContent: ChatCompletionRequest, verbose = false, ): Promise { let aiResponse: ChatCompletionChoice // Set default model if none specified if (!promptWithContent.model) { promptWithContent.model = 'openai/gpt-4o' if (verbose) { console.log('⚠️ No model specified, using default: openai/gpt-4o') } } try { // Create an AbortController for timeout handling const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 180000) // 3 minutes const startTime = Date.now() if (verbose) { console.log(`🚀 Making API request to GitHub Models using ${promptWithContent.model}...`) } const response = await fetch(modelsCompletionsEndpoint, { method: 'post', body: JSON.stringify(promptWithContent), headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, 'X-GitHub-Api-Version': '2022-11-28', Accept: 'application/vnd.github+json', }, signal: controller.signal, }) const fetchTime = Date.now() - startTime if (verbose) { console.log(`⏱️ API response received in ${fetchTime}ms`) } clearTimeout(timeoutId) if (!response.ok) { let errorMessage = `HTTP error! status: ${response.status} - ${response.statusText}` // Try to get more detailed error information try { const errorBody = await response.json() if (errorBody.error && errorBody.error.message) { errorMessage += ` - ${errorBody.error.message}` } } catch { // If we can't parse error body, continue with basic error } // Add helpful hints for common errors if (response.status === 401) { errorMessage += ' (Check your GITHUB_TOKEN)' } else if (response.status === 400) { errorMessage += ' (This may be due to an invalid model or malformed request)' } else if (response.status === 429) { errorMessage += ' (Rate limit exceeded - try again later)' } throw new Error(errorMessage) } const data: ChatCompletionResponse = await response.json() if (!data.choices || data.choices.length === 0) { throw new Error('No response choices returned from API') } aiResponse = data.choices[0] if (verbose) { const totalTime = Date.now() - startTime console.log(`✅ Total API call completed in ${totalTime}ms`) if (data.usage) { console.log( `📊 Tokens: ${data.usage.prompt_tokens} prompt + ${data.usage.completion_tokens} completion = ${data.usage.total_tokens} total`, ) } } } catch (error) { if (error instanceof Error) { if (error.name === 'AbortError') { throw new Error('API call timed out after 3 minutes') } console.error('Error calling GitHub Models REST API:', error.message) } throw error } return cleanAIResponse(aiResponse.message.content) } // Helper function to clean up AI response content function cleanAIResponse(content: string): string { // Remove markdown code blocks return content .replace(/^```[\w]*\n/gm, '') // Remove opening code blocks .replace(/\n```$/gm, '') // Remove closing code blocks at end .replace(/\n```\n/gm, '\n') // Remove standalone closing code blocks .trim() }