tui-scripted-llm.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import type { Context } from 'cordis'
  2. import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
  3. import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
  4. const INITIAL_TEXT = 'I need one decision before I continue.'
  5. const FINAL_TEXT = 'Decision received. Scripted TUI run complete.'
  6. function textChunks(text: string): StreamChunk[] {
  7. return [
  8. { type: 'block-start', index: 0, blockType: 'text' },
  9. ...Array.from(text, (char): StreamChunk => ({ type: 'text-delta', index: 0, text: char })),
  10. { type: 'block-end', index: 0, block: { type: 'text', text } },
  11. { type: 'usage', usage: { inputTokens: 20, outputTokens: text.length } },
  12. { type: 'finish', reason: { kind: 'stop' } },
  13. ]
  14. }
  15. /** Keyless two-step adapter for the real-PTY TUI conversation test. */
  16. class ScriptedTuiAdapter extends LlmAdapter {
  17. async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
  18. const hasToolResult = options.messages.at(-1)?.content.some(block => block.type === 'tool-result') ?? false
  19. if (hasToolResult) {
  20. for (const chunk of textChunks(FINAL_TEXT)) yield chunk
  21. return
  22. }
  23. const args = JSON.stringify({
  24. questions: [{
  25. id: 'mode',
  26. header: 'Execution mode',
  27. question: 'How should the scripted run proceed?',
  28. options: [
  29. { label: 'Safe', description: 'Use the guarded path.' },
  30. { label: 'Fast', description: 'Use the shorter path.' },
  31. ],
  32. }],
  33. })
  34. const callId = CallId('call-ask-mode')
  35. yield { type: 'block-start', index: 0, blockType: 'text' }
  36. for (const char of INITIAL_TEXT) yield { type: 'text-delta', index: 0, text: char }
  37. yield { type: 'block-end', index: 0, block: { type: 'text', text: INITIAL_TEXT } }
  38. yield { type: 'block-start', index: 1, blockType: 'tool-call' }
  39. yield { type: 'tool-call-delta', index: 1, id: callId, name: 'ask_user_question', argumentsDelta: args }
  40. yield {
  41. type: 'block-end',
  42. index: 1,
  43. block: { type: 'tool-call', id: callId, name: 'ask_user_question', arguments: args },
  44. }
  45. yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
  46. yield { type: 'finish', reason: { kind: 'tool-calls' } }
  47. }
  48. }
  49. export const name = 'tui-scripted-llm'
  50. export const inject = ['llm']
  51. /** Register the network-free adapter used by the PTY fixture. */
  52. export function apply(ctx: Context): void {
  53. ctx.llm.registerAdapter(['tui-scripted'], new ScriptedTuiAdapter())
  54. }