feat(cache): add OpenAI prompt_cache_key and Gemini cachedContent support

Provider-specific caching enhancements:

OpenAI:
- Auto-generate prompt_cache_key from message prefix hash
- Key format: omni-{prefix_hash_32chars}
- Preserves client-provided keys (doesn't override)

Gemini:
- Preserve cachedContent ID if provided by client
- Enables explicit Gemini caching for long prompts
- cachedContentTokenCount already tracked in responses
This commit is contained in:
oyi77
2026-03-31 03:55:30 +07:00
parent 0b2c488a61
commit f99c90dc85
4 changed files with 49 additions and 6 deletions
+34 -5
View File
@@ -950,11 +950,24 @@ export async function handleChatCore({
const executeProviderRequest = async (modelToCall = effectiveModel, allowDedup = false) => {
const execute = async () => {
const bodyToSend =
let bodyToSend =
translatedBody.model === modelToCall
? translatedBody
: { ...translatedBody, model: modelToCall };
// Inject prompt_cache_key for OpenAI providers if not already set
if (
targetFormat === FORMATS.OPENAI &&
!bodyToSend.prompt_cache_key &&
Array.isArray(bodyToSend.messages)
) {
const { generatePromptCacheKey } = await import("@/lib/promptCache");
const cacheKey = generatePromptCacheKey(bodyToSend.messages);
if (cacheKey) {
bodyToSend = { ...bodyToSend, prompt_cache_key: cacheKey };
}
}
const rawResult = await withRateLimit(provider, connectionId, modelToCall, () =>
executor.execute({
model: modelToCall,
@@ -1444,11 +1457,19 @@ export async function handleChatCore({
const cachedTokens = toPositiveNumber(
usage.cache_read_input_tokens ??
usage.cached_tokens ??
((usage as Record<string, unknown>).prompt_tokens_details as Record<string, unknown> | undefined)?.cached_tokens
(
(usage as Record<string, unknown>).prompt_tokens_details as
| Record<string, unknown>
| undefined
)?.cached_tokens
);
const cacheCreationTokens = toPositiveNumber(
usage.cache_creation_input_tokens ??
((usage as Record<string, unknown>).prompt_tokens_details as Record<string, unknown> | undefined)?.cache_creation_tokens
(
(usage as Record<string, unknown>).prompt_tokens_details as
| Record<string, unknown>
| undefined
)?.cache_creation_tokens
);
saveRequestUsage({
@@ -1604,11 +1625,19 @@ export async function handleChatCore({
const cachedTokens = toPositiveNumber(
streamUsage.cache_read_input_tokens ??
streamUsage.cached_tokens ??
((streamUsage as Record<string, unknown>).prompt_tokens_details as Record<string, unknown> | undefined)?.cached_tokens
(
(streamUsage as Record<string, unknown>).prompt_tokens_details as
| Record<string, unknown>
| undefined
)?.cached_tokens
);
const cacheCreationTokens = toPositiveNumber(
streamUsage.cache_creation_input_tokens ??
((streamUsage as Record<string, unknown>).prompt_tokens_details as Record<string, unknown> | undefined)?.cache_creation_tokens
(
(streamUsage as Record<string, unknown>).prompt_tokens_details as
| Record<string, unknown>
| undefined
)?.cache_creation_tokens
);
saveRequestUsage({
@@ -52,6 +52,7 @@ type GeminiRequest = {
safetySettings: unknown;
systemInstruction?: GeminiContent;
tools?: Array<{ functionDeclarations: GeminiFunctionDeclaration[] }>;
cachedContent?: string;
};
type CloudCodeEnvelope = {
@@ -82,6 +83,11 @@ function openaiToGeminiBase(model, body, stream) {
safetySettings: DEFAULT_SAFETY_SETTINGS,
};
// Preserve cachedContent if provided by client (for explicit Gemini caching)
if (body.cachedContent) {
result.cachedContent = body.cachedContent;
}
// Generation config
if (body.temperature !== undefined) {
result.generationConfig.temperature = body.temperature;
+1 -1
View File
@@ -1 +1 @@
export { analyzePrefix, shouldInjectCacheControl } from "./prefixAnalyzer";
export { analyzePrefix, shouldInjectCacheControl, generatePromptCacheKey } from "./prefixAnalyzer";
+8
View File
@@ -75,3 +75,11 @@ export function analyzePrefix(messages: Message[]): PrefixAnalysis {
export function shouldInjectCacheControl(analysis: PrefixAnalysis, minTokens = 1024): boolean {
return analysis.prefixTokens >= minTokens && analysis.confidence >= 0.7;
}
export function generatePromptCacheKey(messages: Message[]): string {
const analysis = analyzePrefix(messages);
if (analysis.prefixHash) {
return `omni-${analysis.prefixHash.slice(0, 32)}`;
}
return "";
}