UNPKG

openai-code

Version:

An unofficial proxy layer that lets you use Anthropic Claude Code with any OpenAI API backend.

162 lines (142 loc) 4.91 kB
import { HttpsProxyAgent } from "https-proxy-agent"; import { getEnv } from "./env.mjs" import { OpenAI } from "openai" export function getOpenAICommonOptions() { const options = {}; if (getEnv('PROXY_URL')) { options.httpAgent = new HttpsProxyAgent(getEnv('PROXY_URL')); } return options; } export const getReasoningModel = () => getEnv('REASONING_MODEL') || "o3-mini" const createOpenAiClient = () => { const config = { apiKey: getEnv('OPENAI_CODE_API_KEY'), ...getOpenAICommonOptions(), } const organization = getEnv('OPENAI_CODE_ORGANIZATION_ID') const project = getEnv('OPENAI_CODE_PROJECT_ID') const baseUrl = getEnv('OPENAI_CODE_BASE_URL') if (baseUrl) { config.baseURL = baseUrl } if (organization) { config.organization = organization } if (project) { config.project = project } return new OpenAI(config) } export async function sendOpenAIRequest(args) { const openai = createOpenAiClient(); const openAIArgs = { ...args } // remove temperature for models that don't support it if (openAIArgs.model.startsWith('o')) { if (openAIArgs.messages) { openAIArgs.messages = openAIArgs.messages.map(message => { if (message.role === "system") { return { ...message, role: "developer" } } return message }) } } if (!openAIArgs.tools) { delete openAIArgs.tool_choice } // make the actual API call to OpenAI return openai.chat.completions.create(openAIArgs) } export const answer = async (question, reasoningEffort = "low") => { console.log(`Answering: ${question}`) const completion = await sendOpenAIRequest({ model: getReasoningModel(), reasoning_effort: reasoningEffort, stream: false, messages: [{ role: "developer", content: question }], response_format: { type: "json_object", }, }) const answer = completion.choices[0].message.content console.log(`Answer: ${answer}`) try { return JSON.parse(answer) || {} } catch (error) { return {} } } export async function decide(question) { console.log(`Deciding (boolean) on: ${question}`) if (question.indexOf('"correct":') === -1) { question += `\nMUST respond in parsable JSON. Answer boolean (correctness): { "correct": true }` } let decision = false try { const data = await answer(question, "low") decision = data?.correct === true } catch (error) { decision = false } console.log(`Decision: ${decision ? 'Yes' : 'No'}`) return decision } /** normalizes embeddings to unit vectors for cosine similarity via dot product */ export const normalizeEmbedding = (embedding) => { const norm = Math.sqrt(embedding.reduce((sum, value) => sum + value * value, 0)) // calculate the L2 norm of the embedding if (norm === 0) return embedding // prevent / 0 return embedding.map(value => value / norm) // divide each element by the norm to get a unit vector } export const createEmbedding = async (text) => { const maxAttempts = 5; let attempts = 0; let content = text; const safeLimit = 8191; // maximum tokens allowed while (attempts < maxAttempts) { try { const openai = createOpenAiClient(); const response = await openai.embeddings.create({ model: getEnv('OPENAI_CODE_EMBEDDING_MODEL') || "text-embedding-3-small", encoding_format: "float", input: content, }); const embedding = response.data[0].embedding; return normalizeEmbedding(embedding); } catch (error) { // Check for errors indicating too many tokens in the context if ( error.message.includes("maximum context length") || error.message.includes("requested") ) { // Attempt to extract the requested token count from the error message const match = error.message.match(/requested (\d+) tokens/); const requestedTokens = match?.[1] ? Number.parseInt(match[1], 10) : null; let newLength; if (requestedTokens) { // Compute a safe ratio using a safety margin (e.g., 0.95) const ratio = safeLimit / requestedTokens; const safeRatio = ratio * 0.95; newLength = Math.floor(content.length * safeRatio); } else { // Fallback: reduce the content by a fixed 10% newLength = Math.floor(content.length * 0.9); } // If the new length is zero or did not change, return null. if (newLength === 0 || newLength === content.length) { return null; } console.warn( `Trimming content: reducing length from ${content.length} to ${newLength}.` ); content = content.slice(0, newLength); attempts++; } else { // For any non context-length error, return null immediately. return null; } } } // If all attempts are exhausted, return null. return null; }