This commit is contained in:
@@ -5,6 +5,7 @@ import { ToolDef } from '../../llm/openai-compat.js';
|
||||
import type { ToolContext, ToolResult } from './core.js';
|
||||
import { resolveAndGuard, resolveOutputPathWithin } from './core.js';
|
||||
import { logger } from '../../logger.js';
|
||||
import { recordLlmUsage } from '../../llm/usage-recorder.js';
|
||||
|
||||
// --- Supported image extensions ---
|
||||
|
||||
@@ -310,7 +311,9 @@ export async function callVisionModel(
|
||||
}
|
||||
|
||||
const json = (await response.json()) as {
|
||||
model?: string;
|
||||
choices?: Array<{ message?: { content?: string } }>;
|
||||
usage?: { prompt_tokens?: number; completion_tokens?: number };
|
||||
};
|
||||
|
||||
const content = json.choices?.[0]?.message?.content;
|
||||
@@ -318,6 +321,20 @@ export async function callVisionModel(
|
||||
return { output: 'Vision API returned no content', isError: true };
|
||||
}
|
||||
|
||||
// This vision call is a raw fetch outside OpenAICompatClient, so it would
|
||||
// otherwise miss the per-user usage ledger. Record it directly (issue
|
||||
// #499). Vision uses its own endpoint directly (never the gateway).
|
||||
let route = 'unknown';
|
||||
try { route = new URL(visionBaseUrl).host || 'unknown'; } catch { /* keep 'unknown' */ }
|
||||
recordLlmUsage({
|
||||
userId: ctx.userId ?? 'local',
|
||||
source: 'direct',
|
||||
model: json.model || visionModel,
|
||||
route,
|
||||
tokensIn: json.usage?.prompt_tokens ?? 0,
|
||||
tokensOut: json.usage?.completion_tokens ?? 0,
|
||||
});
|
||||
|
||||
return { output: content, isError: false };
|
||||
} catch (e) {
|
||||
if ((e as Error).name === 'AbortError') {
|
||||
|
||||
Reference in New Issue
Block a user