/** * REAL-composition proof: the shipped gateway YAML shape (session + * projection registry + credentials + openrouter-usage) boots through the * vendored Loader, the service default-export survives, a key resolved from * the credentials document lets a mocked OpenRouter fetch populate the * pricing table and the balance, and a logged step serves a priced * `openRouterCost` view through the composed registry. */ import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { pathToFileURL } from 'node:url' import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import Loader from '@deepseek-ai/cordis-plugin-loader' import Include from '@deepseek-ai/cordis-plugin-include' import { createMessage } from '@deepseek-ai/dsh-llm' import SessionStore from '@deepseek-ai/dsh-session' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' import LocalCredentialProvider from '@deepseek-ai/dsh-credentials-local' import FileSettingsProvider from '@deepseek-ai/dsh-settings-file' import OpenRouterUsageGateway from '@deepseek-ai/dsh-openrouter-usage' let root: string | undefined let context: Context | undefined afterEach(async () => { await context?.fiber.dispose() context = undefined if (root !== undefined) await rm(root, { recursive: true, force: true }) root = undefined vi.unstubAllGlobals() }) /** Mock OpenRouter's read endpoints and record the calls. */ function stubOpenRouter() { const pricing = [ { id: 'deepseek/deepseek-chat', pricing: { prompt: '0.0000014', completion: '0.0000028', request: '0' } }, ] const credits = { total_credits: 42, total_usage: 1, is_free_tier: false } const calls: string[] = [] vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL) => { const url = String(input) calls.push(url) if (url.endsWith('/models')) { return new Response(JSON.stringify({ data: pricing }), { status: 200 }) } if (url.endsWith('/credits')) { return new Response(JSON.stringify({ data: { ...credits } }), { status: 200 }) } if (url.endsWith('/auth/key')) { return new Response(JSON.stringify({ data: { label: 'test', usage: 1, limit: 1000, is_free_tier: false }, }), { status: 200 }) } return new Response('not found', { status: 404 }) })) return { calls, credits } } async function loadComposition(): Promise { let rootDir = root if (rootDir === undefined) { rootDir = await mkdtemp(join(tmpdir(), 'dsh-openrouter-composition-')) root = rootDir } await writeFile(join(rootDir, '.credentials.yaml'), 'OPENROUTER_API_KEY: sk-openrouter-test\n', { mode: 0o600 }) const settingsPath = join(rootDir, 'settings.yaml') await writeFile(settingsPath, '# test settings\n') await writeFile(join(rootDir, 'cordis.yml'), [ "- name: '@deepseek-ai/dsh-session'", "- name: '@deepseek-ai/dsh-session-projection'", '- id: settings', " name: '@deepseek-ai/dsh-settings-file'", ' config:', ` path: ${JSON.stringify(settingsPath)}`, ' debounceMs: 10', '- id: credentials', " name: '@deepseek-ai/dsh-credentials-local'", ' config:', ` path: ${JSON.stringify(join(rootDir, '.credentials.yaml'))}`, ' debounceMs: 10', "- name: '@deepseek-ai/dsh-openrouter-usage'", ' config:', ' syncEnabled: false', '', ].join('\n')) const ctx = new Context() context = ctx ctx.baseUrl = pathToFileURL(rootDir).href + '/' await ctx.plugin(Loader) ctx.loader.builtins.include = Include const modules = new Map([ ['@deepseek-ai/dsh-session', SessionStore], ['@deepseek-ai/dsh-session-projection', SessionProjectionRegistry], ['@deepseek-ai/dsh-settings-file', FileSettingsProvider], ['@deepseek-ai/dsh-credentials-local', LocalCredentialProvider], ['@deepseek-ai/dsh-openrouter-usage', OpenRouterUsageGateway], ]) ctx.loader.internal = { version: 'v2', async import(specifier: string) { if (!modules.has(specifier)) throw new Error(`unexpected Loader import: ${specifier}`) return modules.get(specifier) }, } as unknown as NonNullable await ctx.loader.create({ name: 'cordis:include', config: { path: pathToFileURL(join(rootDir, 'cordis.yml')).href }, }) await ctx.loader.await() return ctx } describe('openrouter-usage real composition', () => { it('resolves the key, fetches pricing, and prices a logged step through the composed registry', async () => { const openRouter = stubOpenRouter() const loaded = await loadComposition() // Give the gateway's refresh a moment to run against the mock. await vi.waitFor(() => { expect(openRouter.calls.some(url => url.endsWith('/models'))).toBe(true) }, { timeout: 5000 }) const session = loaded.sessions.create() session.append('request/context', { provider: 'openrouter', model: 'deepseek/deepseek-chat' }) session.append('turn/start', { turn: 1 }) session.append('step/start', { turn: 1, step: 1 }) session.append('assistant/chunk', { turn: 1, step: 1, chunk: { type: 'usage', usage: { inputTokens: 1_000, outputTokens: 200 } }, }) session.append('assistant/message', { turn: 1, step: 1, message: createMessage({ role: 'assistant', content: [{ type: 'text', text: 'hi' }], source: { kind: 'model', provider: 'openrouter', model: 'deepseek/deepseek-chat' }, }), usage: { inputTokens: 1_000, outputTokens: 200 }, }, { surfaceOp: 'append', sourceEventSeqs: [session.events.length - 1] }) session.append('step/end', { turn: 1, step: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) const cost = loaded.sessionProjections.snapshot(session).values.openRouterCost expect(cost).toMatchObject({ totalUsd: 1000 * 1.4e-6 + 200 * 2.8e-6, pricedSteps: 1, unknownModelSteps: 0, currency: 'USD', }) }) it('serves the account balance snapshot through the Remote gateway', async () => { const openRouter = stubOpenRouter() const loaded = await loadComposition() await vi.waitFor(() => { expect(openRouter.calls.some(url => url.endsWith('/credits'))).toBe(true) }, { timeout: 5000 }) const balance = loaded.openRouterUsage.snapshot() expect(balance.balanceUsd).toBe(41) expect(balance.label).toBe('test') expect(balance.currency).toBe('USD') expect(balance.updatedAt).not.toBeNull() }) it('re-fetches the account on demand and serves the moved figure', async () => { const openRouter = stubOpenRouter() const loaded = await loadComposition() await vi.waitFor(() => { expect(openRouter.calls.some(url => url.endsWith('/credits'))).toBe(true) }, { timeout: 5000 }) expect(loaded.openRouterUsage.snapshot().balanceUsd).toBe(41) openRouter.credits.total_usage = 12 await expect(loaded.openRouterUsage.refresh()).resolves.toMatchObject({ balanceUsd: 30 }) expect(loaded.openRouterUsage.snapshot().balanceUsd).toBe(30) }) it('shares one in-flight fetch across concurrent on-demand refreshes', async () => { const openRouter = stubOpenRouter() const loaded = await loadComposition() await vi.waitFor(() => { expect(openRouter.calls.some(url => url.endsWith('/credits'))).toBe(true) }, { timeout: 5000 }) const before = openRouter.calls.filter(url => url.endsWith('/credits')).length const [first, second] = await Promise.all([ loaded.openRouterUsage.refresh(), loaded.openRouterUsage.refresh(), ]) expect(first).toEqual(second) expect(openRouter.calls.filter(url => url.endsWith('/credits')).length).toBe(before + 1) // The shared promise is released once it settles, so a later click fetches again. await loaded.openRouterUsage.refresh() expect(openRouter.calls.filter(url => url.endsWith('/credits')).length).toBe(before + 2) }) })