/** * Real-composition guard for the dormant pi-ai posture: LlmRuntime, * settings-file, credentials-local, and a bare `llm-pi-ai` row boot from a * test-only cordis.yml through the actual Loader + Include path, an external * edit of settings.yaml registers the route live, and the next request * carries the credential the credentials document supplies. A hand-mounted `ctx.plugin` cannot * catch Loader export-shape failures, which is why the twin adapter has the * same guard. */ 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 LlmRuntime, { createMessage, createUserMessage } from '@deepseek-ai/dsh-llm' import LocalCredentialProvider from '@deepseek-ai/dsh-credentials-local' import FileSettingsProvider from '@deepseek-ai/dsh-settings-file' import * as LlmPiAi from '@deepseek-ai/dsh-llm-pi-ai' import { assemble } from './assemble.ts' import { closeMockServers, mockServer, textEvents } from './mock-server.ts' /** One text block, then a tool call truncated by the output-token ceiling. */ const truncatedToolCallEvents = [ '{"choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}', '{"choices":[{"delta":{"content":"partial"},"index":0,"finish_reason":null}]}', '{"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call-1","type":"function","function":{"name":"echo","arguments":"{\\"text\\":"}}]},"index":0,"finish_reason":null}]}', '{"choices":[{"delta":{},"index":0,"finish_reason":"length"}],"usage":{"prompt_tokens":3,"completion_tokens":4}}', '[DONE]', ] 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 await closeMockServers() vi.unstubAllEnvs() }) /** Boot the dormant composition: a bare `llm-pi-ai` row with no config at all. */ async function loadComposition(): Promise<{ ctx: Context; settingsPath: string }> { root = await mkdtemp(join(tmpdir(), 'dsh-pi-composition-')) const settingsPath = join(root, 'settings.yaml') await writeFile(settingsPath, '# personal settings\n') await writeFile(join(root, '.credentials.yaml'), 'PI_COMPOSITION_KEY: key-from-store\n', { mode: 0o600 }) const configPath = join(root, 'cordis.yml') await writeFile(configPath, [ '- id: llm', " name: 'test-llm-service'", '- 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(root, '.credentials.yaml'))}`, ' debounceMs: 10', '- id: llm-pi-ai', " name: '@deepseek-ai/dsh-llm-pi-ai'", '', ].join('\n')) const ctx = new Context() context = ctx ctx.baseUrl = pathToFileURL(root).href + '/' await ctx.plugin(Loader) ctx.loader.builtins.include = Include const modules = new Map([ ['test-llm-service', LlmRuntime], ['@deepseek-ai/dsh-settings-file', FileSettingsProvider], ['@deepseek-ai/dsh-credentials-local', LocalCredentialProvider], ['@deepseek-ai/dsh-llm-pi-ai', LlmPiAi], ]) 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(configPath).href }, }) await ctx.loader.await() return { ctx, settingsPath } } describe('llm-pi-ai real dormant composition', () => { it('boots with zero routes and registers one the moment settings supply a profile', async () => { vi.stubEnv('PI_COMPOSITION_KEY', '') const server = await mockServer([{ events: textEvents }]) const { ctx, settingsPath } = await loadComposition() // The shipped posture: the adapter exists, no route does. expect(ctx.llm.listProviders()).toEqual([]) // Exactly what the web Models page leaves on disk. await writeFile(settingsPath, [ 'llm-pi-ai:', ' providers:', ' deepseek:', ' apiKeyEnv: PI_COMPOSITION_KEY', ` baseURL: ${server.url}`, '', ].join('\n')) await vi.waitFor(() => { expect(ctx.llm.listProviders().map(provider => provider.id)).toEqual(['deepseek']) }, { timeout: 5000 }) const result = await assemble(ctx, { provider: 'deepseek', model: 'deepseek-v4-flash', messages: [] }) expect(result.message.content).toEqual([{ type: 'text', text: 'hello' }]) expect(server.headers[0]?.authorization).toBe('Bearer key-from-store') }) it('continues natively after max-token assembly drops a tool call, with pruned replay metadata', async () => { vi.stubEnv('PI_COMPOSITION_KEY', '') const server = await mockServer([ { events: truncatedToolCallEvents }, { events: textEvents }, ]) const { ctx, settingsPath } = await loadComposition() await writeFile(settingsPath, [ 'llm-pi-ai:', ' providers:', ' deepseek:', ' apiKeyEnv: PI_COMPOSITION_KEY', ` baseURL: ${server.url}`, '', ].join('\n')) await vi.waitFor(() => { expect(ctx.llm.listProviders().map(provider => provider.id)).toEqual(['deepseek']) }, { timeout: 5000 }) const truncated = await assemble(ctx, { provider: 'deepseek', model: 'deepseek-v4-flash', messages: [], }) expect(truncated.finish).toEqual({ kind: 'max-tokens' }) expect(truncated.message.content).toEqual([{ type: 'text', text: 'partial' }]) expect(truncated.message.source).toEqual({ kind: 'model', provider: 'deepseek', model: 'deepseek-v4-flash', replayState: { response: { kind: 'pi-ai', version: 2, api: 'openai-completions', provider: 'deepseek', model: 'deepseek-v4-flash', stopReason: 'length', }, blocks: [{ type: 'text' }], }, }) const continued = await assemble(ctx, { provider: 'deepseek', model: 'deepseek-v4-flash', messages: [ truncated.message, createUserMessage({ content: [{ type: 'text', text: 'continue' }], source: { kind: 'user' } }), ], }) expect(continued.message.content).toEqual([{ type: 'text', text: 'hello' }]) expect(server.requests).toHaveLength(2) expect(server.requests[1]).toMatchObject({ messages: [ { role: 'assistant', content: 'partial' }, { role: 'user', content: 'continue' }, ], }) const followup = server.requests[1] as { messages?: unknown[] } expect(followup.messages?.[0]).not.toHaveProperty('tool_calls') }) it('continues a legacy session whose stored replay state no longer matches its content', async () => { vi.stubEnv('PI_COMPOSITION_KEY', '') const server = await mockServer([{ events: textEvents }]) const { ctx, settingsPath } = await loadComposition() await writeFile(settingsPath, [ 'llm-pi-ai:', ' providers:', ' deepseek:', ' apiKeyEnv: PI_COMPOSITION_KEY', ` baseURL: ${server.url}`, '', ].join('\n')) await vi.waitFor(() => { expect(ctx.llm.listProviders().map(provider => provider.id)).toEqual(['deepseek']) }, { timeout: 5000 }) // A pre-envelope session log entry: max-token assembly dropped the tool // call from content while the flat v1 state still describes both blocks. const poisoned = createMessage({ role: 'assistant', content: [{ type: 'text', text: 'partial' }], source: { kind: 'model', ...{ provider: 'deepseek', model: 'deepseek-v4-flash', replayState: { kind: 'pi-ai', version: 1, api: 'openai-completions', provider: 'deepseek', model: 'deepseek-v4-flash', stopReason: 'length', blocks: [{ type: 'text' }, { type: 'tool-call' }], }, }, }, }) const continued = await assemble(ctx, { provider: 'deepseek', model: 'deepseek-v4-flash', messages: [ poisoned, createUserMessage({ content: [{ type: 'text', text: 'continue' }], source: { kind: 'user' } }), ], }) expect(continued.finish).toEqual({ kind: 'stop' }) expect(continued.message.content).toEqual([{ type: 'text', text: 'hello' }]) expect(server.requests[0]).toMatchObject({ messages: [ { role: 'assistant', content: 'partial' }, { role: 'user', content: 'continue' }, ], }) }) })