Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/backend/convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type * as lib_eventClaimCoverage from "../lib/eventClaimCoverage.js";
import type * as lib_openai from "../lib/openai.js";
import type * as mbfc from "../mbfc.js";
import type * as migrations from "../migrations.js";
import type * as pipelineDiagnostics from "../pipelineDiagnostics.js";
import type * as privateData from "../privateData.js";
import type * as prompts from "../prompts.js";
import type * as seeds from "../seeds.js";
Expand Down Expand Up @@ -76,6 +77,7 @@ declare const fullApi: ApiFromModules<{
"lib/openai": typeof lib_openai;
mbfc: typeof mbfc;
migrations: typeof migrations;
pipelineDiagnostics: typeof pipelineDiagnostics;
privateData: typeof privateData;
prompts: typeof prompts;
seeds: typeof seeds;
Expand Down
30 changes: 29 additions & 1 deletion packages/backend/convex/aiBudget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import type { MutationCtx, QueryCtx } from "./_generated/server";

/** Default model pricing as of 2025. Add new models as needed. */
const DEFAULT_MODEL_RATES: Record<string, { input: number; output: number }> = {
"gpt-5-nano": { input: 0.00000005, output: 0.0000004 },
"gpt-5-mini": { input: 0.00000025, output: 0.000002 },
"gpt-4o-mini": { input: 0.00000015, output: 0.0000006 },
"gpt-4o": { input: 0.0000025, output: 0.00001 },
"gpt-4.1-nano": { input: 0.0000001, output: 0.0000004 },
Expand Down Expand Up @@ -67,14 +69,33 @@ export function calculateCost(
model: string,
inputTokens: number,
outputTokens: number,
): number {
return calculateCostWithCachedInput(model, inputTokens, 0, outputTokens);
}

export function calculateCostWithCachedInput(
model: string,
inputTokens: number,
cachedInputTokens: number,
outputTokens: number,
): number {
const rates = MODEL_RATES[model];
const cachedInputRate = rates ? rates.input * 0.1 : undefined;
if (!rates) {
// Unknown model — use gpt-4o-mini rates as conservative fallback
const fallback = MODEL_RATES["gpt-4o-mini"]!;
return inputTokens * fallback.input + outputTokens * fallback.output;
}
return inputTokens * rates.input + outputTokens * rates.output;
const safeCachedInputTokens = Math.min(
Math.max(0, cachedInputTokens),
Math.max(0, inputTokens),
);
const billableInputTokens = Math.max(0, inputTokens - safeCachedInputTokens);
return (
billableInputTokens * rates.input +
safeCachedInputTokens * (cachedInputRate ?? rates.input) +
outputTokens * rates.output
);
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -329,6 +350,7 @@ export const logUsage = internalMutation({
callType: v.optional(v.string()),
inputTokens: v.number(),
outputTokens: v.number(),
cachedInputTokens: v.optional(v.number()),
costUsd: v.number(),
eventId: v.optional(v.id("events")),
articleId: v.optional(v.id("articles")),
Expand All @@ -350,6 +372,7 @@ async function recordUsageInternal(
callType: string;
inputTokens: number;
outputTokens: number;
cachedInputTokens?: number;
costUsd: number;
eventId?: Id<"events">;
articleId?: Id<"articles">;
Expand Down Expand Up @@ -391,6 +414,7 @@ async function recordUsageInternal(
callType: args.callType,
inputTokens: args.inputTokens,
outputTokens: args.outputTokens,
cachedInputTokens: args.cachedInputTokens,
costUsd: args.costUsd,
eventId: args.eventId,
articleId: args.articleId,
Expand All @@ -414,6 +438,7 @@ export const recordUsage = internalMutation({
model: v.string(),
inputTokens: v.number(),
outputTokens: v.number(),
cachedInputTokens: v.optional(v.number()),
costUsd: v.number(),
eventId: v.optional(v.id("events")),
articleId: v.optional(v.id("articles")),
Expand Down Expand Up @@ -449,6 +474,7 @@ export const getTodaysUsage = internalQuery({
calls: number;
inputTokens: number;
outputTokens: number;
cachedInputTokens: number;
costUsd: number;
latencyMs: number;
};
Expand All @@ -464,12 +490,14 @@ export const getTodaysUsage = internalQuery({
calls: 0,
inputTokens: 0,
outputTokens: 0,
cachedInputTokens: 0,
costUsd: 0,
latencyMs: 0,
};
existing.calls++;
existing.inputTokens += row.inputTokens;
existing.outputTokens += row.outputTokens;
existing.cachedInputTokens += row.cachedInputTokens ?? 0;
existing.costUsd += row.costUsd;
existing.latencyMs += row.latencyMs ?? 0;
group[key] = existing;
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/convex/claimDivergenceNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
type ClaimType,
} from "./prompts";

const DEFAULT_MODEL = "gpt-4o-mini";
const DEFAULT_MODEL = "gpt-5-nano";
const DEFAULT_ENABLED = true;
const DEFAULT_BATCH_SIZE = 4;
const DEFAULT_SCAN_LIMIT = 60;
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/convex/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export const seedDefaults = internalMutation({
},
{
key: "event_summary_model",
value: "gpt-4o-mini",
value: "gpt-5-nano",
description:
"OpenAI chat model used for event perspective summaries.",
},
Expand Down Expand Up @@ -468,7 +468,7 @@ export const seedDefaults = internalMutation({
},
{
key: "article_fact_extraction_model",
value: "gpt-4o-mini",
value: "gpt-5-nano",
description:
"OpenAI chat model used to extract atomic facts from articles during enrichment.",
},
Expand Down Expand Up @@ -498,7 +498,7 @@ export const seedDefaults = internalMutation({
},
{
key: "article_bias_detection_model",
value: "gpt-4o-mini",
value: "gpt-5-nano",
description:
"OpenAI chat model used for per-article bias component scoring during enrichment.",
},
Expand Down Expand Up @@ -552,7 +552,7 @@ export const seedDefaults = internalMutation({
},
{
key: "claim_analysis_model",
value: "gpt-4o-mini",
value: "gpt-5-nano",
description:
"OpenAI chat model used for event-level claim divergence analysis.",
},
Expand Down
Loading