| | 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<string> { |
| | let aiResponse: ChatCompletionChoice |
| |
|
| | |
| | if (!promptWithContent.model) { |
| | promptWithContent.model = 'openai/gpt-4o' |
| | if (verbose) { |
| | console.log('⚠️ No model specified, using default: openai/gpt-4o') |
| | } |
| | } |
| |
|
| | try { |
| | |
| | const controller = new AbortController() |
| | const timeoutId = setTimeout(() => controller.abort(), 180000) |
| |
|
| | 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 { |
| | const errorBody = await response.json() |
| | if (errorBody.error && errorBody.error.message) { |
| | errorMessage += ` - ${errorBody.error.message}` |
| | } |
| | } catch { |
| | |
| | } |
| |
|
| | |
| | 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) |
| | } |
| |
|
| | |
| | function cleanAIResponse(content: string): string { |
| | |
| | return content |
| | .replace(/^```[\w]*\n/gm, '') |
| | .replace(/\n```$/gm, '') |
| | .replace(/\n```\n/gm, '\n') |
| | .trim() |
| | } |
| |
|