54 lines
3.5 KiB
TypeScript
54 lines
3.5 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import { EventEmitter } from 'node:events'
|
|
import { mkdtemp, readFile, writeFile, rm } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
import { contextObserverSource, observeOmpContextSamples, prepareContextObserver } from '../src/main/omp-context-observer'
|
|
import type { OmpKernel } from '../src/main/omp'
|
|
|
|
const directory = await mkdtemp(join(tmpdir(), 'ycode-observer-'))
|
|
const previous = { config: process.env.YCODE_CONTEXT_REQUEST_FILE, sample: process.env.YCODE_CONTEXT_SAMPLE_FILE }
|
|
let stop: (() => void) | undefined
|
|
try {
|
|
const paths = await prepareContextObserver(directory, 'session')
|
|
process.env.YCODE_CONTEXT_REQUEST_FILE = paths.configPath
|
|
process.env.YCODE_CONTEXT_SAMPLE_FILE = paths.samplePath
|
|
await writeFile(paths.configPath, JSON.stringify({ runId: 'run', sessionPath: '/fixture.jsonl', modelId: 'selected', modelName: 'fixture', origins: [], toolCategories: {} }))
|
|
const emitter = new EventEmitter(), frames: Array<{ type: string; breakdown?: { sequence: number } }> = []
|
|
stop = observeOmpContextSamples(emitter as OmpKernel, paths.samplePath)
|
|
emitter.on('event', frame => frames.push(frame))
|
|
const extension = await import(pathToFileURL(paths.extensionPath).href)
|
|
let hook: (event: unknown, ctx: unknown) => void = () => { throw Error('hook missing') }
|
|
extension.default({ on(name: string, callback: typeof hook) { assert.equal(name, 'before_provider_request'); hook = callback } })
|
|
const event = { payload: { messages: [{ role: 'user', content: 'POISON_NEVER_PERSIST_PROMPT' }], tools: [] } }
|
|
const context = { sessionManager: { getSessionFile: () => '/fixture.jsonl' }, model: { id: 'fixture' } }
|
|
const write = process.stdout.write
|
|
let stdoutCalls = 0
|
|
try { process.stdout.write = (() => { stdoutCalls++; return true }) as typeof process.stdout.write; hook(event, context) }
|
|
finally { process.stdout.write = write }
|
|
assert.equal(stdoutCalls, 0, 'observer must not write into the native JSONL channel')
|
|
assert.ok(!contextObserverSource().includes('process.stdout'), 'extension cannot interleave its data with an image frame')
|
|
const sample = await readFile(paths.samplePath, 'utf8')
|
|
assert.ok(!sample.includes('POISON_NEVER_PERSIST_PROMPT'), 'only counts and identity leave the request observer')
|
|
emitter.emit('event', { type: 'message_end' })
|
|
assert.equal(frames[0].type, 'ycode_context_breakdown', 'sample is observed before request-end usage')
|
|
assert.equal(frames[0].breakdown?.sequence, 1)
|
|
emitter.emit('event', { type: 'message_end' })
|
|
assert.equal(frames.filter(frame => frame.type === 'ycode_context_breakdown').length, 1, 'duplicate notification does not clear a newer provider total')
|
|
hook(event, context)
|
|
emitter.emit('event', { type: 'message_end' })
|
|
assert.equal(frames.at(-2)?.breakdown?.sequence, 2, 'new prepared input precedes the next provider usage frame')
|
|
stop(); stop = undefined
|
|
const count = frames.length
|
|
hook(event, context)
|
|
emitter.emit('event', { type: 'message_end' })
|
|
assert.equal(frames.length, count + 1, 'retired observers stop injecting sample frames')
|
|
console.log('CONTEXT_OBSERVER_TRANSPORT_PASS 7 checks')
|
|
} finally {
|
|
stop?.()
|
|
if (previous.config === undefined) delete process.env.YCODE_CONTEXT_REQUEST_FILE; else process.env.YCODE_CONTEXT_REQUEST_FILE = previous.config
|
|
if (previous.sample === undefined) delete process.env.YCODE_CONTEXT_SAMPLE_FILE; else process.env.YCODE_CONTEXT_SAMPLE_FILE = previous.sample
|
|
await rm(directory, { recursive: true, force: true })
|
|
}
|