llm: LlmCallConfig + callConfigEquals + deepFreeze — the stateless request vocabulary

The call configuration (model + sampling scalars) becomes named
vocabulary: per-conversation state that the session log records as part
of the request header (the reconstructability RFC on this branch), with
callConfigEquals as the real-change detector behind logged header deltas
and deepFreeze as the ownership helper the loop applies to every built
request. dsh-llm stays stateless — request in, chunks out; no
conversation object lives here.
This commit is contained in:
Tianyi Cui
2026-07-06 02:27:54 +08:00
parent 8510986909
commit 3f09159ab8
4 changed files with 115 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
/**
* call-config unit tests: field-wise LlmCallConfig equality (the real-change
* detector behind logged header deltas) and the deepFreeze ownership helper
* the loop applies to every built request.
*/
import { describe, expect, it } from 'vitest'
import { callConfigEquals, deepFreeze } from '../src/call-config.ts'
describe('callConfigEquals', () => {
it('compares every field, including the stop list element-wise', () => {
expect(callConfigEquals({ model: 'm' }, { model: 'm' })).toBe(true)
expect(callConfigEquals({ model: 'm' }, { model: 'x' })).toBe(false)
expect(callConfigEquals({ model: 'm', temperature: 0.5 }, { model: 'm' })).toBe(false)
expect(callConfigEquals({ model: 'm', maxTokens: 1 }, { model: 'm', maxTokens: 2 })).toBe(false)
expect(callConfigEquals({ model: 'm', stop: ['a'] }, { model: 'm' })).toBe(false)
expect(callConfigEquals({ model: 'm', stop: ['a'] }, { model: 'm', stop: ['a', 'b'] })).toBe(false)
expect(callConfigEquals({ model: 'm', stop: ['a'] }, { model: 'm', stop: ['b'] })).toBe(false)
expect(callConfigEquals({ model: 'm', stop: ['a', 'b'] }, { model: 'm', stop: ['a', 'b'] })).toBe(true)
})
})
describe('deepFreeze', () => {
it('freezes nested structure in place and returns the same reference', () => {
const value = { a: { b: [1, { c: 'x' }] } }
const frozen = deepFreeze(value)
expect(frozen).toBe(value)
expect(Object.isFrozen(value)).toBe(true)
expect(Object.isFrozen(value.a)).toBe(true)
expect(Object.isFrozen(value.a.b)).toBe(true)
expect(Object.isFrozen(value.a.b[1])).toBe(true)
// ESM runs in strict mode: mutation throws rather than silently failing.
expect(() => { (value.a.b[1] as { c: string }).c = 'y' }).toThrow(TypeError)
})
it('passes primitives through and terminates on cycles', () => {
expect(deepFreeze(42)).toBe(42)
expect(deepFreeze(null)).toBeNull()
const cyclic = { self: undefined as unknown }
cyclic.self = cyclic
deepFreeze(cyclic)
expect(Object.isFrozen(cyclic)).toBe(true)
})
})