Files
deepseek-harness/packages/llm/openrouter-usage/tests/loader-composition.spec.ts
Coder 8719fa84f0
Some checks failed
CI / windows node 24 / wine blocking (push) Has been skipped
CI / node 22.19 (push) Has been skipped
CI / node 26 (push) Has been skipped
CI / python 3.10 / keyless SDK (push) Has been skipped
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / wine apt cache (push) Successful in 7s
CI / serial / linux (push) Has been skipped
Deploy documentation / build (push) Failing after 1m25s
Deploy documentation / deploy (push) Has been skipped
Landlock Run / Matrix (push) Successful in 5s
Release (vendor) / Pack npm tarballs (push) Failing after 2m47s
Release (dsh) / Pack npm tarballs (push) Failing after 1m56s
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Failing after 1m57s
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Failing after 1m19s
Release (vendor) / Publish to npm (push) Has been skipped
Release (dsh) / Publish to npm (push) Has been skipped
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
Landlock Run / darwin (no platform package — degradation proof) (push) Has been cancelled
Landlock Run / ${{ matrix.platform }} (push) Has been cancelled
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Failing after 1m24s
feat: add economy/maximum presets, tool-lab and subagent-cursor extensions
- new economy and maximum agent presets with three-role pipeline skill
- new packages/extensions/tool-lab (home-lab ComfyUI/Docling/Whishper tools)
- new packages/subagent/subagent-cursor provider
- openrouter balance UI with on-demand refresh
- session projection context-seed boundary fold
- regenerate docs catalogs; keep local searxng benchmark scripts
2026-08-23 22:12:30 +07:00

208 lines
7.9 KiB
TypeScript

/**
* 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<Context> {
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<string, unknown>([
['@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<typeof ctx.loader.internal>
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)
})
})