diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index c60b379a0e..5c3312ab87 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -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).prompt_tokens_details as Record | undefined)?.cached_tokens + ( + (usage as Record).prompt_tokens_details as + | Record + | undefined + )?.cached_tokens ); const cacheCreationTokens = toPositiveNumber( usage.cache_creation_input_tokens ?? - ((usage as Record).prompt_tokens_details as Record | undefined)?.cache_creation_tokens + ( + (usage as Record).prompt_tokens_details as + | Record + | 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).prompt_tokens_details as Record | undefined)?.cached_tokens + ( + (streamUsage as Record).prompt_tokens_details as + | Record + | undefined + )?.cached_tokens ); const cacheCreationTokens = toPositiveNumber( streamUsage.cache_creation_input_tokens ?? - ((streamUsage as Record).prompt_tokens_details as Record | undefined)?.cache_creation_tokens + ( + (streamUsage as Record).prompt_tokens_details as + | Record + | undefined + )?.cache_creation_tokens ); saveRequestUsage({ diff --git a/open-sse/translator/request/openai-to-gemini.ts b/open-sse/translator/request/openai-to-gemini.ts index 4aa3bba95f..8d4d2b4047 100644 --- a/open-sse/translator/request/openai-to-gemini.ts +++ b/open-sse/translator/request/openai-to-gemini.ts @@ -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; diff --git a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx index 3c5d685530..bf173b8b4b 100644 --- a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx @@ -106,7 +106,10 @@ export default function AppearanceTab() { { id: "cyan", color: COLOR_THEMES.cyan, label: t("themeCyan") }, ]; - const sidebarSections = SIDEBAR_SECTIONS.map((section) => ({ + const showDebug = settings.debugMode === true; + const sidebarSections = SIDEBAR_SECTIONS.filter( + (section) => section.visibility !== "debug" || showDebug + ).map((section) => ({ ...section, title: getSidebarLabel(section.titleKey, section.titleFallback), items: section.items.map((item) => ({ ...item, label: tSidebar(item.i18nKey) })), diff --git a/src/app/api/cache/entries/route.ts b/src/app/api/cache/entries/route.ts index 26f30b02a9..97ff51ca9a 100644 --- a/src/app/api/cache/entries/route.ts +++ b/src/app/api/cache/entries/route.ts @@ -1,5 +1,6 @@ import { NextRequest, NextResponse } from "next/server"; import { getDbInstance } from "@/lib/db/core"; +import { isAuthenticated } from "@/shared/utils/apiAuth"; interface CacheEntry { id: string; @@ -12,6 +13,10 @@ interface CacheEntry { } export async function GET(req: NextRequest) { + if (!(await isAuthenticated(req))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + try { const { searchParams } = new URL(req.url); const page = Math.max(1, parseInt(searchParams.get("page") || "1", 10)); @@ -71,6 +76,10 @@ export async function GET(req: NextRequest) { } export async function DELETE(req: NextRequest) { + if (!(await isAuthenticated(req))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + try { const { searchParams } = new URL(req.url); const signature = searchParams.get("signature"); diff --git a/src/app/api/cache/route.ts b/src/app/api/cache/route.ts index d1bca53891..dd7364f929 100644 --- a/src/app/api/cache/route.ts +++ b/src/app/api/cache/route.ts @@ -9,15 +9,21 @@ import { } from "@/lib/semanticCache"; import { getIdempotencyStats } from "@/lib/idempotencyLayer"; import { getCacheMetrics, getCacheTrend } from "@/lib/db/settings"; +import { isAuthenticated } from "@/shared/utils/apiAuth"; function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } export async function GET(req: NextRequest) { + if (!(await isAuthenticated(req))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + try { const { searchParams } = new URL(req.url); - const trendHours = parseInt(searchParams.get("trendHours") || "24", 10); + const rawHours = parseInt(searchParams.get("trendHours") || "24", 10); + const trendHours = Math.min(720, Math.max(1, Number.isNaN(rawHours) ? 24 : rawHours)); const cacheStats = getCacheStats(); const idempotencyStats = getIdempotencyStats(); @@ -36,6 +42,10 @@ export async function GET(req: NextRequest) { } export async function DELETE(req: NextRequest) { + if (!(await isAuthenticated(req))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + try { const { searchParams } = new URL(req.url); const model = searchParams.get("model"); diff --git a/src/lib/db/settings.ts b/src/lib/db/settings.ts index 333d71379f..5173e81e14 100644 --- a/src/lib/db/settings.ts +++ b/src/lib/db/settings.ts @@ -630,7 +630,7 @@ export async function getCacheMetrics() { totalCachedTokens: totalsRow?.totalCachedTokens || 0, totalCacheCreationTokens: totalsRow?.totalCacheCreationTokens || 0, tokensSaved, - estimatedCostSaved: 0, // Would need pricing data to calculate + estimatedCostSaved, byProvider, byStrategy, lastUpdated: new Date().toISOString(), diff --git a/src/lib/promptCache/index.ts b/src/lib/promptCache/index.ts index f4ee1fd501..2d1446f864 100644 --- a/src/lib/promptCache/index.ts +++ b/src/lib/promptCache/index.ts @@ -1 +1 @@ -export { analyzePrefix, shouldInjectCacheControl } from "./prefixAnalyzer"; +export { analyzePrefix, shouldInjectCacheControl, generatePromptCacheKey } from "./prefixAnalyzer"; diff --git a/src/lib/promptCache/prefixAnalyzer.ts b/src/lib/promptCache/prefixAnalyzer.ts index 43f622c87f..6f8e86e12e 100644 --- a/src/lib/promptCache/prefixAnalyzer.ts +++ b/src/lib/promptCache/prefixAnalyzer.ts @@ -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 ""; +} diff --git a/src/shared/components/Sidebar.tsx b/src/shared/components/Sidebar.tsx index fb77a3017a..2eeb5a3b65 100644 --- a/src/shared/components/Sidebar.tsx +++ b/src/shared/components/Sidebar.tsx @@ -40,7 +40,7 @@ export default function Sidebar({ useEffect(() => { const applySettings = (data) => { - setShowDebug(data?.enableRequestLogs === true); + setShowDebug(data?.debugMode === true); setHiddenSidebarItems(normalizeHiddenSidebarItems(data?.[HIDDEN_SIDEBAR_ITEMS_SETTING_KEY])); }; @@ -52,8 +52,8 @@ export default function Sidebar({ const handleSettingsUpdated = (event: Event) => { const detail = (event as CustomEvent>).detail || {}; - if ("enableRequestLogs" in detail) { - setShowDebug(detail.enableRequestLogs === true); + if ("debugMode" in detail) { + setShowDebug(detail.debugMode === true); } if (HIDDEN_SIDEBAR_ITEMS_SETTING_KEY in detail) {