diff --git a/src/tools/imagegen.ts b/src/tools/imagegen.ts index 99efb08..4aabb5a 100644 --- a/src/tools/imagegen.ts +++ b/src/tools/imagegen.ts @@ -21,6 +21,8 @@ import type { ContentLibrary } from '../content/library.js'; import { checkImageBudget, recordImageAsset } from '../content/record-image.js'; import { ModelClient } from '../agent/llm.js'; import { analyzeMediaRequest, renderProposalForAskUser } from '../agent/media-router.js'; +import { recordUsage } from '../stats/tracker.js'; +import { findModel, estimateCostUsd } from '../gateway-models.js'; interface ImageGenInput { prompt: string; @@ -220,6 +222,20 @@ function buildExecute(deps: ImageGenDeps) { const sizeKB = (fileSize / 1024).toFixed(1); const revisedPrompt = imageData.revised_prompt ? `\nRevised prompt: ${imageData.revised_prompt}` : ''; + // Stats: record this generation so it shows up in `franklin insights` + // alongside chat spend. Before this, media generations bypassed + // recordUsage entirely (only LLM chat calls were tracked), so the + // insights panel under-reported total spend and never surfaced + // image-generation models in its "top models" list. Fire-and-forget — + // stats write must not fail a user-visible generation. + void (async () => { + try { + const m = await findModel(imageModel); + const estCost = m ? estimateCostUsd(m, { quantity: 1 }) : 0; + recordUsage(imageModel, 0, 0, estCost, 0); + } catch { /* ignore stats errors */ } + })(); + let contentSummary = ''; if (contentId && deps.library) { const rec = recordImageAsset(deps.library, { diff --git a/src/tools/videogen.ts b/src/tools/videogen.ts index 2fba8b7..d6c22ce 100644 --- a/src/tools/videogen.ts +++ b/src/tools/videogen.ts @@ -36,6 +36,8 @@ import { loadChain, API_URLS, VERSION } from '../config.js'; import type { ContentLibrary } from '../content/library.js'; import { ModelClient } from '../agent/llm.js'; import { analyzeMediaRequest, renderProposalForAskUser } from '../agent/media-router.js'; +import { recordUsage } from '../stats/tracker.js'; +import { findModel, estimateCostUsd } from '../gateway-models.js'; interface VideoGenInput { prompt: string; @@ -290,6 +292,21 @@ function buildExecute(deps: VideoGenDeps) { const sizeMB = (fileSize / 1_048_576).toFixed(1); const dur = videoData.duration_seconds ?? duration; + // Stats: record this generation so it shows up in `franklin insights` + // alongside chat spend. Before this, media generations bypassed + // recordUsage entirely, so the insights panel under-reported total + // spend and never surfaced video models in its "top models" list. + // Prefer the live gateway price when the model is in the catalog; + // fall back to the legacy $0.05/s estimate otherwise. Fire-and- + // forget — stats write must not fail a user-visible generation. + void (async () => { + try { + const m = await findModel(videoModel); + const estCost = m ? estimateCostUsd(m, { duration_seconds: dur }) : estimateVideoCostUsd(dur); + recordUsage(videoModel, 0, 0, estCost, 0); + } catch { /* ignore stats errors */ } + })(); + let contentSummary = ''; if (contentId && deps.library) { const rec = deps.library.addAsset(contentId, {