mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
132 lines
4.1 KiB
JavaScript
132 lines
4.1 KiB
JavaScript
// Gemini helper functions for translator
|
|
|
|
// Unsupported JSON Schema constraints that should be removed for Antigravity
|
|
export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [
|
|
"minLength", "maxLength", "exclusiveMinimum", "exclusiveMaximum",
|
|
"pattern", "minItems", "maxItems", "format",
|
|
"default", "examples", "$schema", "const"
|
|
];
|
|
|
|
// Default safety settings
|
|
export const DEFAULT_SAFETY_SETTINGS = [
|
|
{ category: "HARM_CATEGORY_HATE_SPEECH", threshold: "OFF" },
|
|
{ category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "OFF" },
|
|
{ category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "OFF" },
|
|
{ category: "HARM_CATEGORY_HARASSMENT", threshold: "OFF" },
|
|
{ category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "OFF" }
|
|
];
|
|
|
|
// Convert OpenAI content to Gemini parts
|
|
export function convertOpenAIContentToParts(content) {
|
|
const parts = [];
|
|
|
|
if (typeof content === "string") {
|
|
parts.push({ text: content });
|
|
} else if (Array.isArray(content)) {
|
|
for (const item of content) {
|
|
if (item.type === "text") {
|
|
parts.push({ text: item.text });
|
|
} else if (item.type === "image_url" && item.image_url?.url?.startsWith("data:")) {
|
|
const match = item.image_url.url.match(/^data:([^;]+);base64,(.+)$/);
|
|
if (match) {
|
|
parts.push({
|
|
inlineData: { mime_type: match[1], data: match[2] }
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return parts;
|
|
}
|
|
|
|
// Extract text content from OpenAI content
|
|
export function extractTextContent(content) {
|
|
if (typeof content === "string") return content;
|
|
if (Array.isArray(content)) {
|
|
return content.filter(c => c.type === "text").map(c => c.text).join("");
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// Try parse JSON safely
|
|
export function tryParseJSON(str) {
|
|
if (typeof str !== "string") return str;
|
|
try {
|
|
return JSON.parse(str);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Generate request ID
|
|
export function generateRequestId() {
|
|
return `agent-${crypto.randomUUID()}`;
|
|
}
|
|
|
|
// Generate session ID
|
|
export function generateSessionId() {
|
|
return `-${Math.floor(Math.random() * 9000000000000000000)}`;
|
|
}
|
|
|
|
// Generate project ID
|
|
export function generateProjectId() {
|
|
const adjectives = ["useful", "bright", "swift", "calm", "bold"];
|
|
const nouns = ["fuze", "wave", "spark", "flow", "core"];
|
|
const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
return `${adj}-${noun}-${crypto.randomUUID().slice(0, 5)}`;
|
|
}
|
|
|
|
// Clean JSON Schema for Antigravity API compatibility - removes unsupported keywords recursively
|
|
export function cleanJSONSchemaForAntigravity(schema) {
|
|
if (!schema || typeof schema !== "object") return schema;
|
|
|
|
const cleaned = Array.isArray(schema) ? [] : {};
|
|
|
|
for (const [key, value] of Object.entries(schema)) {
|
|
if (UNSUPPORTED_SCHEMA_CONSTRAINTS.includes(key)) continue;
|
|
|
|
// Handle type array like ["string", "null"] - Gemini only supports single type
|
|
if (key === "type" && Array.isArray(value)) {
|
|
const nonNullType = value.find(t => t !== "null") || "string";
|
|
cleaned[key] = nonNullType;
|
|
continue;
|
|
}
|
|
|
|
if (value && typeof value === "object") {
|
|
cleaned[key] = cleanJSONSchemaForAntigravity(value);
|
|
} else {
|
|
cleaned[key] = value;
|
|
}
|
|
}
|
|
|
|
// Cleanup required fields - only keep fields that exist in properties
|
|
if (cleaned.required && Array.isArray(cleaned.required) && cleaned.properties) {
|
|
const validRequired = cleaned.required.filter(field =>
|
|
Object.prototype.hasOwnProperty.call(cleaned.properties, field)
|
|
);
|
|
if (validRequired.length === 0) {
|
|
delete cleaned.required;
|
|
} else {
|
|
cleaned.required = validRequired;
|
|
}
|
|
}
|
|
|
|
// Add placeholder for empty object schemas (Antigravity requirement)
|
|
if (cleaned.type === "object") {
|
|
if (!cleaned.properties || Object.keys(cleaned.properties).length === 0) {
|
|
cleaned.properties = {
|
|
reason: {
|
|
type: "string",
|
|
description: "Brief explanation of why you are calling this tool"
|
|
}
|
|
};
|
|
cleaned.required = ["reason"];
|
|
}
|
|
}
|
|
|
|
return cleaned;
|
|
}
|
|
|