| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import { describe, expect, it } from 'vitest'
- import type { HistoryEntry } from '@deepseek-ai/dsh-client-connection/client'
- import { createAssistantMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
- import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
- import { inspectRequests } from '../src/client/sessions/request-inspection.ts'
- const at = (seq: number, type: string, data: unknown): SessionEvent =>
- ({ seq, time: 1_700_000_000_000 + seq, type, data }) as SessionEvent
- const entriesOf = (events: readonly SessionEvent[]): HistoryEntry[] =>
- events.map(event => ({ event }))
- describe('inspectRequests', () => {
- it('projects ordinary and compaction calls into one chronological request stream', () => {
- const events = [
- at(0, 'step/start', { turn: 1, step: 1 }),
- at(1, 'request/header', {
- reason: 'initial',
- header: {
- config: { provider: 'fake', model: 'model' },
- system: 'system',
- tools: [{
- name: 'read',
- description: 'Read a file.',
- parameters: { type: 'object' },
- }],
- },
- }),
- at(2, 'tool/call', {
- turn: 1,
- step: 1,
- callId: 'call-1',
- name: 'read',
- arguments: '{}',
- }),
- at(3, 'assistant/message', {
- turn: 1,
- step: 1,
- message: createAssistantMessage({
- content: [{ type: 'text', text: 'done' }],
- source: { provider: 'fake', model: 'model' },
- }),
- usage: { inputTokens: 5, outputTokens: 2 },
- }),
- at(4, 'step/end', { turn: 1, step: 1 }),
- at(5, 'compact/start', { turn: 1 }),
- at(6, 'compact/summary', {
- summary: [{ type: 'text', text: 'summary' }],
- rawOutput: [
- { type: 'reasoning', text: 'thought' },
- { type: 'text', text: 'summary' },
- ],
- provider: 'fake',
- model: 'compact-model',
- usage: { inputTokens: 8, outputTokens: 3 },
- }),
- at(7, 'user/message', createUserMessage({
- content: [{ type: 'text', text: 'checkpoint' }],
- source: { kind: 'plugin', plugin: 'compact' },
- })),
- at(8, 'compact/end', { turn: 1 }),
- ]
- const snapshot = inspectRequests(entriesOf(events))
- expect(snapshot.requests).toMatchObject([
- {
- purpose: 'assistant',
- startSeq: 0,
- resultSeq: 3,
- status: 'complete',
- prompt: {
- config: { provider: 'fake', model: 'model' },
- system: 'system',
- },
- promptChange: { seq: 1, kind: 'initial' },
- },
- {
- purpose: 'compaction',
- startSeq: 5,
- resultSeq: 6,
- replacementSeq: 7,
- status: 'complete',
- summary: [{ type: 'text', text: 'summary' }],
- },
- ])
- expect(snapshot.callSchemas.get('call-1')?.name).toBe('read')
- })
- it('captures schemas for nested tool dispatches from the active request header', () => {
- const snapshot = inspectRequests(entriesOf([
- at(0, 'request/header', {
- reason: 'initial',
- header: {
- config: { provider: 'fake', model: 'model' },
- tools: [{
- name: 'read',
- description: 'Read a file.',
- parameters: { type: 'object' },
- }],
- },
- }),
- at(1, 'tool/code-dispatch-start', {
- parentCallId: 'parent',
- subCallId: 'nested',
- name: 'read',
- arguments: {},
- }),
- ]))
- expect(snapshot.callSchemas.get('nested')?.name).toBe('read')
- })
- it('keeps chunk-reported usage through request failure and prefers it to message fallback', () => {
- const chunkUsage = { inputTokens: 21, outputTokens: 3 }
- const retryUsage = {
- inputTokens: 5,
- outputTokens: 2,
- cacheReadTokens: 8,
- reasoningTokens: 1,
- }
- const snapshot = inspectRequests(entriesOf([
- at(0, 'step/start', { turn: 1, step: 1 }),
- at(1, 'assistant/chunk', {
- turn: 1,
- step: 1,
- chunk: { type: 'usage', usage: chunkUsage },
- }),
- at(2, 'llm/retry', {
- turn: 1,
- step: 1,
- retry: 1,
- maxRetries: 2,
- delayMs: 100,
- failure: { message: 'rate limited' },
- }),
- at(3, 'assistant/chunk', {
- turn: 1,
- step: 1,
- chunk: { type: 'usage', usage: retryUsage },
- }),
- at(4, 'assistant/message', {
- turn: 1,
- step: 1,
- message: createAssistantMessage({
- content: [{ type: 'text', text: 'recovered' }],
- source: { provider: 'fake', model: 'model' },
- }),
- usage: { inputTokens: 1, outputTokens: 1 },
- }),
- ]))
- expect(snapshot.requests[0]).toMatchObject({
- status: 'complete',
- usage: {
- inputTokens: 26,
- outputTokens: 5,
- cacheReadTokens: 8,
- reasoningTokens: 1,
- },
- })
- })
- it('treats a scrubbed durable-fixture tool catalog as unavailable', () => {
- const snapshot = inspectRequests(entriesOf([
- at(0, 'step/start', { turn: 1, step: 1 }),
- at(1, 'request/header', {
- reason: 'initial',
- header: {
- config: { provider: 'fake', model: 'model' },
- tools: '{{tools}}',
- },
- }),
- at(2, 'tool/call', {
- turn: 1,
- step: 1,
- callId: 'call-1',
- name: 'read',
- arguments: '{}',
- }),
- ]))
- expect(snapshot.callSchemas).toEqual(new Map())
- expect(snapshot.requests[0]?.prompt?.tools).toEqual([])
- })
- })
|