(
messages: ChatMessage[],
options?: { maxTokens?: number; temperature?: number }
)
| 15 | * Works with OpenAI, Ollama, LM Studio, together.ai, etc. |
| 16 | */ |
| 17 | export async function callAI( |
| 18 | messages: ChatMessage[], |
| 19 | options?: { maxTokens?: number; temperature?: number } |
| 20 | ): Promise<AIResponse> { |
| 21 | const config = await loadConfig(); |
| 22 | const apiKey = process.env.DEVCTX_AI_KEY || config.aiApiKey; |
| 23 | const baseUrl = process.env.DEVCTX_AI_PROVIDER || config.aiProvider; |
| 24 | const model = process.env.DEVCTX_AI_MODEL || config.aiModel; |
| 25 | |
| 26 | if (!apiKey && baseUrl.includes("openai.com")) { |
| 27 | return { |
| 28 | content: "", |
| 29 | error: |
| 30 | "No API key configured. Set DEVCTX_AI_KEY env var or run: devctx config set aiApiKey <key>", |
| 31 | }; |
| 32 | } |
| 33 | |
| 34 | try { |
| 35 | const url = `${baseUrl.replace(/\/+$/, "")}/chat/completions`; |
| 36 | |
| 37 | const headers: Record<string, string> = { |
| 38 | "Content-Type": "application/json", |
| 39 | }; |
| 40 | if (apiKey) { |
| 41 | headers["Authorization"] = `Bearer ${apiKey}`; |
| 42 | } |
| 43 | |
| 44 | const response = await fetch(url, { |
| 45 | method: "POST", |
| 46 | headers, |
| 47 | body: JSON.stringify({ |
| 48 | model, |
| 49 | messages, |
| 50 | max_tokens: options?.maxTokens || 1024, |
| 51 | temperature: options?.temperature ?? 0.3, |
| 52 | }), |
| 53 | }); |
| 54 | |
| 55 | if (!response.ok) { |
| 56 | const errorText = await response.text(); |
| 57 | return { |
| 58 | content: "", |
| 59 | error: `AI API error (${response.status}): ${errorText.slice(0, 200)}`, |
| 60 | }; |
| 61 | } |
| 62 | |
| 63 | const data = (await response.json()) as any; |
| 64 | const content = data.choices?.[0]?.message?.content || ""; |
| 65 | |
| 66 | return { content }; |
| 67 | } catch (err: any) { |
| 68 | return { |
| 69 | content: "", |
| 70 | error: `AI request failed: ${err.message}`, |
| 71 | }; |
| 72 | } |
| 73 | } |
no test coverage detected