| |
| |
| |
| import { fetchAndEncode } from './remoteimage'; |
| import { z } from 'zod'; |
| import { ToolRegistry } from '@google/gemini-cli-core/dist/src/tools/tool-registry.js'; |
|
|
| |
| type Part = { text?: string; inlineData?: { mimeType: string; data: string } }; |
|
|
| |
| async function callLocalFunction(_name: string, _args: unknown) { |
| return { ok: true }; |
| } |
|
|
| |
| |
| |
| export async function mapRequest(body: any) { |
| const parts: Part[] = []; |
|
|
| |
| for (const m of body.messages) { |
| if (Array.isArray(m.content)) { |
| for (const item of m.content) { |
| if (item.type === 'image_url') { |
| parts.push({ inlineData: await fetchAndEncode(item.image_url.url) }); |
| } else if (item.type === 'text') { |
| parts.push({ text: item.text }); |
| } |
| } |
| } else { |
| parts.push({ text: m.content }); |
| } |
| } |
|
|
| |
| const generationConfig: Record<string, unknown> = { |
| temperature: body.temperature, |
| maxOutputTokens: body.max_tokens, |
| topP: body.top_p, |
| ...(body.generationConfig ?? {}), |
| }; |
| if (body.include_reasoning === true) { |
| generationConfig.enable_thoughts = true; |
| generationConfig.thinking_budget ??= 2048; |
| } |
|
|
| |
| if (body.include_reasoning === true && generationConfig.thinking !== true) { |
| generationConfig.thinking = true; |
| generationConfig.thinking_budget ??= 2048; |
| } |
| generationConfig.maxInputTokens ??= 1_000_000; |
|
|
| const geminiReq = { |
| contents: [{ role: 'user', parts }], |
| generationConfig, |
| stream: body.stream, |
| }; |
|
|
| |
| const tools = new ToolRegistry({} as any); |
|
|
| if (body.functions?.length) { |
| const reg = tools as any; |
| body.functions.forEach((fn: any) => |
| reg.registerTool( |
| fn.name, |
| { |
| title: fn.name, |
| description: fn.description ?? '', |
| inputSchema: z.object(fn.parameters?.properties ?? {}), |
| }, |
| async (args: unknown) => callLocalFunction(fn.name, args), |
| ), |
| ); |
| } |
|
|
| return { geminiReq, tools }; |
| } |
|
|
| |
| |
| |
| export function mapResponse(gResp: any) { |
| const usage = gResp.usageMetadata ?? {}; |
| return { |
| id: `chatcmpl-${Date.now()}`, |
| object: 'chat.completion', |
| created: Math.floor(Date.now() / 1000), |
| model: 'gemini-2.5-pro-latest', |
| choices: [ |
| { |
| index: 0, |
| message: { role: 'assistant', content: gResp.text }, |
| finish_reason: 'stop', |
| }, |
| ], |
| usage: { |
| prompt_tokens: usage.promptTokens ?? 0, |
| completion_tokens: usage.candidatesTokens ?? 0, |
| total_tokens: usage.totalTokens ?? 0, |
| }, |
| }; |
| } |
|
|
| |
| |
| |
|
|
| export function mapStreamChunk(chunk: any) { |
| const part = chunk?.candidates?.[0]?.content?.parts?.[0] ?? {}; |
| const delta: any = { role: 'assistant' }; |
|
|
| if (part.thought === true) { |
| delta.content = `<think>${part.text ?? ''}`; |
| } else if (typeof part.text === 'string') { |
| delta.content = part.text; |
| } |
| return { choices: [ { delta, index: 0 } ] }; |
| } |
|
|
|
|
|
|