Support camelCase Gemini params and validate vision
This commit is contained in:
@@ -30,6 +30,7 @@ export const transformOutboundPayload: RequestPreprocessor = async (req) => {
|
||||
}
|
||||
|
||||
applyMistralPromptFixes(req);
|
||||
applyGoogleAIKeyTransforms(req);
|
||||
|
||||
// Native prompts are those which were already provided by the client in the
|
||||
// target API format. We don't need to transform them.
|
||||
@@ -87,3 +88,43 @@ function applyMistralPromptFixes(req: Request): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toSnakeCase(str: string): string {
|
||||
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
||||
}
|
||||
|
||||
function transformKeysToSnakeCase(obj: any, hasTransformed = { value: false }): any {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(item => transformKeysToSnakeCase(item, hasTransformed));
|
||||
}
|
||||
|
||||
if (obj !== null && typeof obj === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).map(([key, value]) => {
|
||||
const snakeKey = toSnakeCase(key);
|
||||
if (snakeKey !== key) {
|
||||
hasTransformed.value = true;
|
||||
}
|
||||
return [
|
||||
snakeKey,
|
||||
transformKeysToSnakeCase(value, hasTransformed)
|
||||
];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function applyGoogleAIKeyTransforms(req: Request): void {
|
||||
// Google (Gemini) API in their infinite wisdom accepts both snake_case and camelCase
|
||||
// even though in the docs they use snake_case. Some frontends (e.g. ST) use camelCase
|
||||
// so we normalize all keys to snake_case here
|
||||
if (req.outboundApi === "google-ai") {
|
||||
const hasTransformed = { value: false };
|
||||
req.body = transformKeysToSnakeCase(req.body, hasTransformed);
|
||||
if (hasTransformed.value) {
|
||||
req.log.info("Applied Gemini camelCase -> snake_case transform");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { assertNever } from "../../../../shared/utils";
|
||||
import { RequestPreprocessor } from "../index";
|
||||
import { containsImageContent as containsImageContentOpenAI } from "../../../../shared/api-schemas/openai";
|
||||
import { containsImageContent as containsImageContentAnthropic } from "../../../../shared/api-schemas/anthropic";
|
||||
import { containsImageContent as containsImageContentGoogleAI } from "../../../../shared/api-schemas/google-ai";
|
||||
import { ForbiddenError } from "../../../../shared/errors";
|
||||
|
||||
/**
|
||||
@@ -25,8 +26,10 @@ export const validateVision: RequestPreprocessor = async (req) => {
|
||||
case "anthropic-chat":
|
||||
hasImage = containsImageContentAnthropic(req.body.messages);
|
||||
break;
|
||||
case "anthropic-text":
|
||||
case "google-ai":
|
||||
hasImage = containsImageContentGoogleAI(req.body.contents);
|
||||
break;
|
||||
case "anthropic-text":
|
||||
case "mistral-ai":
|
||||
case "mistral-text":
|
||||
case "openai-image":
|
||||
|
||||
Reference in New Issue
Block a user