mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
Merge pull request #834 from oyi77/feat/cache-page-prompt-cache-tracking
fix(debug/sidebar): debug toggle and sidebar visibility
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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) })),
|
||||
|
||||
9
src/app/api/cache/entries/route.ts
vendored
9
src/app/api/cache/entries/route.ts
vendored
@@ -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");
|
||||
|
||||
12
src/app/api/cache/route.ts
vendored
12
src/app/api/cache/route.ts
vendored
@@ -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");
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { analyzePrefix, shouldInjectCacheControl } from "./prefixAnalyzer";
|
||||
export { analyzePrefix, shouldInjectCacheControl, generatePromptCacheKey } from "./prefixAnalyzer";
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
|
||||
@@ -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<Record<string, unknown>>).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) {
|
||||
|
||||
Reference in New Issue
Block a user