diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ccb558..e26be5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to @blockrun/llm will be documented in this file. +## Unreleased + +### Fixed + +- **Export `VideoClient` from the package entry point.** The class was + fully implemented in `src/video.ts` and documented in the README + (`import { VideoClient } from '@blockrun/llm'`), but the export was + missing from `src/index.ts`. Downstream consumers (Franklin, + franklin-canvas) had to hand-roll their own x402 + polling loop + against `/v1/videos/generations` even though a working client was + already shipped — they just couldn't reach it. Restores the + promised public surface; no source changes to `video.ts`. + ## 2.1.0 ### Added diff --git a/src/index.ts b/src/index.ts index 3b69bc6..3204b78 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ export { LLMClient, default } from "./client"; export { ImageClient } from "./image"; export { MusicClient } from "./music"; +export { VideoClient } from "./video"; export { SearchClient } from "./search"; export { XClient, diff --git a/test/unit/cost-log.test.ts b/test/unit/cost-log.test.ts index 34ef282..957b9e5 100644 --- a/test/unit/cost-log.test.ts +++ b/test/unit/cost-log.test.ts @@ -4,11 +4,18 @@ import * as path from 'path'; import * as os from 'os'; import { logCost, getCostSummary } from '../../src/cost-log.js'; -const COST_LOG = path.join(os.homedir(), '.blockrun', 'data', 'costs.jsonl'); +// `src/cost-log.ts` was rewritten to share the on-wire schema with +// Franklin's AgentClient — new path is `~/.blockrun/cost_log.jsonl`, +// new schema uses `ts` / `endpoint` / `cost_usd` instead of the +// previous `timestamp` / `costUsd` / `inputTokens`. These tests track +// the current shape. +const COST_LOG = path.join(os.homedir(), '.blockrun', 'cost_log.jsonl'); const BACKUP = COST_LOG + '.bak'; describe('Cost Log Module', () => { beforeEach(() => { + // Move any real on-disk log out of the way so tests run against an + // empty file and don't permanently destroy the user's ledger. if (fs.existsSync(COST_LOG)) { fs.copyFileSync(COST_LOG, BACKUP); fs.unlinkSync(COST_LOG); @@ -24,18 +31,18 @@ describe('Cost Log Module', () => { it('should log a cost entry', () => { logCost({ - timestamp: '2026-03-22T10:00:00Z', + ts: Date.now() / 1000, + endpoint: '/v1/chat/completions', + cost_usd: 0.002, model: 'openai/gpt-5.4', - inputTokens: 100, - outputTokens: 50, - costUsd: 0.002, }); expect(fs.existsSync(COST_LOG)).toBe(true); const content = fs.readFileSync(COST_LOG, 'utf-8').trim(); const entry = JSON.parse(content); expect(entry.model).toBe('openai/gpt-5.4'); - expect(entry.costUsd).toBe(0.002); + expect(entry.cost_usd).toBe(0.002); + expect(entry.endpoint).toBe('/v1/chat/completions'); }); it('should return empty summary when no logs', () => { @@ -46,9 +53,9 @@ describe('Cost Log Module', () => { }); it('should summarize multiple entries', () => { - logCost({ timestamp: '', model: 'openai/gpt-5.4', inputTokens: 100, outputTokens: 50, costUsd: 0.01 }); - logCost({ timestamp: '', model: 'anthropic/claude-sonnet-4.6', inputTokens: 200, outputTokens: 100, costUsd: 0.02 }); - logCost({ timestamp: '', model: 'openai/gpt-5.4', inputTokens: 50, outputTokens: 25, costUsd: 0.005 }); + logCost({ ts: 1, endpoint: '/v1/chat/completions', cost_usd: 0.01, model: 'openai/gpt-5.4' }); + logCost({ ts: 2, endpoint: '/v1/chat/completions', cost_usd: 0.02, model: 'anthropic/claude-sonnet-4.6' }); + logCost({ ts: 3, endpoint: '/v1/chat/completions', cost_usd: 0.005, model: 'openai/gpt-5.4' }); const summary = getCostSummary(); expect(summary.calls).toBe(3);