mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
- Bug B1-B7: media UI m.kind||m.type, serviceKinds, gemini mediaPriority, schema kind, models/info lookup by kind - Dead code D1-D6: safeParseJSON, drop PROVIDER_ENDPOINTS, orphan fetcher, GITHUB_CONFIG derive, getProviderConfig internal, legacy kiro file - Translator concerns: toOpenAIUsage, toOpenAIFinish (gemini/kiro/ollama + fix kiro tool finish), thinking effort maps - Reorg helpers/ → concerns/ (logic) + formats/ (per-format) + schema/ (pure enums: roles/blocks/finishReasons/defaults) - Wire ~280 hardcoded role/block/finish/default literals to schema enums across 20+ files - collapseTextParts + extractTextContent dedup - Normalize translator fn names to openaiToXRequest / xToOpenAIResponse - Golden tests lock behavior; 0 regression (byte-for-byte providers/alias, 26=26 known fails) Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
4.0 KiB
JavaScript
151 lines
4.0 KiB
JavaScript
import { register } from "../index.js";
|
|
import { FORMATS } from "../formats.js";
|
|
import { adjustMaxTokens } from "../formats/maxTokens.js";
|
|
import { encodeDataUri } from "../concerns/image.js";
|
|
import { collapseTextParts } from "../concerns/message.js";
|
|
import { ROLE, GEMINI_ROLE, OPENAI_BLOCK } from "../schema/index.js";
|
|
|
|
// Convert Gemini request to OpenAI format
|
|
export function geminiToOpenAIRequest(model, body, stream) {
|
|
const result = {
|
|
model: model,
|
|
messages: [],
|
|
stream: stream
|
|
};
|
|
|
|
// Generation config
|
|
if (body.generationConfig) {
|
|
const config = body.generationConfig;
|
|
if (config.maxOutputTokens) {
|
|
const tempBody = { max_tokens: config.maxOutputTokens, tools: body.tools };
|
|
result.max_tokens = adjustMaxTokens(tempBody);
|
|
}
|
|
if (config.temperature !== undefined) {
|
|
result.temperature = config.temperature;
|
|
}
|
|
if (config.topP !== undefined) {
|
|
result.top_p = config.topP;
|
|
}
|
|
}
|
|
|
|
// System instruction
|
|
if (body.systemInstruction) {
|
|
const systemText = extractGeminiText(body.systemInstruction);
|
|
if (systemText) {
|
|
result.messages.push({
|
|
role: ROLE.SYSTEM,
|
|
content: systemText
|
|
});
|
|
}
|
|
}
|
|
|
|
// Convert contents to messages
|
|
if (body.contents && Array.isArray(body.contents)) {
|
|
for (const content of body.contents) {
|
|
const converted = convertGeminiContent(content);
|
|
if (converted) {
|
|
result.messages.push(converted);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tools
|
|
if (body.tools && Array.isArray(body.tools)) {
|
|
result.tools = [];
|
|
for (const tool of body.tools) {
|
|
if (tool.functionDeclarations) {
|
|
for (const func of tool.functionDeclarations) {
|
|
result.tools.push({
|
|
type: OPENAI_BLOCK.FUNCTION,
|
|
function: {
|
|
name: func.name,
|
|
description: func.description || "",
|
|
parameters: func.parameters || { type: "object", properties: {} }
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Convert Gemini content to OpenAI message
|
|
function convertGeminiContent(content) {
|
|
const role = content.role === GEMINI_ROLE.USER ? ROLE.USER : ROLE.ASSISTANT;
|
|
|
|
if (!content.parts || !Array.isArray(content.parts)) {
|
|
return null;
|
|
}
|
|
|
|
const parts = [];
|
|
const toolCalls = [];
|
|
|
|
for (const part of content.parts) {
|
|
if (part.text !== undefined) {
|
|
parts.push({ type: OPENAI_BLOCK.TEXT, text: part.text });
|
|
}
|
|
|
|
if (part.inlineData) {
|
|
parts.push({
|
|
type: OPENAI_BLOCK.IMAGE_URL,
|
|
image_url: {
|
|
url: encodeDataUri(part.inlineData.mimeType, part.inlineData.data)
|
|
}
|
|
});
|
|
}
|
|
|
|
if (part.functionCall) {
|
|
toolCalls.push({
|
|
id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
type: OPENAI_BLOCK.FUNCTION,
|
|
function: {
|
|
name: part.functionCall.name,
|
|
arguments: JSON.stringify(part.functionCall.args || {})
|
|
}
|
|
});
|
|
}
|
|
|
|
if (part.functionResponse) {
|
|
return {
|
|
role: ROLE.TOOL,
|
|
tool_call_id: part.functionResponse.id || part.functionResponse.name,
|
|
content: JSON.stringify(part.functionResponse.response?.result || part.functionResponse.response || {})
|
|
};
|
|
}
|
|
}
|
|
|
|
if (toolCalls.length > 0) {
|
|
const result = { role: ROLE.ASSISTANT };
|
|
if (parts.length > 0) {
|
|
result.content = parts.length === 1 ? parts[0].text : parts;
|
|
}
|
|
result.tool_calls = toolCalls;
|
|
return result;
|
|
}
|
|
|
|
if (parts.length > 0) {
|
|
return {
|
|
role,
|
|
content: collapseTextParts(parts)
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Extract text from Gemini content
|
|
function extractGeminiText(content) {
|
|
if (typeof content === "string") return content;
|
|
if (content.parts && Array.isArray(content.parts)) {
|
|
return content.parts.map(p => p.text || "").join("");
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// Register
|
|
register(FORMATS.GEMINI, FORMATS.OPENAI, geminiToOpenAIRequest, null);
|
|
register(FORMATS.GEMINI_CLI, FORMATS.OPENAI, geminiToOpenAIRequest, null);
|
|
|