harness.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import { Context } from '@deepseek-ai/cordis'
  2. import LlmRuntime, { CallId } from '@deepseek-ai/dsh-llm'
  3. import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
  4. import ToolRuntime from '@deepseek-ai/dsh-tools'
  5. import type { Agent } from '@deepseek-ai/dsh-agent'
  6. import SubagentRuntime from '@deepseek-ai/dsh-subagent'
  7. import { Session, SessionId } from '@deepseek-ai/dsh-session'
  8. import * as mock from './scripted-provider.ts'
  9. import * as tool from '../src/index.ts'
  10. /** Shared non-aborted tool signal for package-local integration tests. */
  11. export const testToolSignal = new AbortController().signal
  12. /** Build the minimal parent Agent owned by the package-local scripted provider. */
  13. export function fakeAgent(id = 'parent-1'): Agent {
  14. const sessionId = SessionId(id)
  15. return { id: sessionId, options: {}, session: Session.create(sessionId) } as unknown as Agent
  16. }
  17. /** Mount the real tool and service stack around one scripted subagent provider. */
  18. export async function setup(toolConfig: tool.Config, mockConfig: Partial<mock.Config> = {}): Promise<Context> {
  19. const ctx = new Context()
  20. await ctx.plugin(LlmRuntime)
  21. await ctx.plugin(SystemPrompt)
  22. await ctx.plugin(ToolRuntime)
  23. await ctx.plugin(SubagentRuntime)
  24. await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
  25. await ctx.plugin(tool, toolConfig)
  26. return ctx
  27. }
  28. let callCounter = 0
  29. /** Execute the registered subagent tool through the real ToolRuntime pipeline. */
  30. export function callSubagent(
  31. ctx: Context,
  32. args: unknown,
  33. over: { agent?: Agent | undefined; signal?: AbortSignal } = {},
  34. ) {
  35. // Distinguish "no override" (use a default agent) from an explicit
  36. // `{ agent: undefined }` (test the no-agent path). Under
  37. // exactOptionalPropertyTypes the key is omitted rather than set to undefined.
  38. const agent = 'agent' in over ? over.agent : fakeAgent()
  39. return ctx.tools.execute({
  40. signal: testToolSignal,
  41. callId: CallId(`call-${++callCounter}`),
  42. name: 'subagent',
  43. arguments: args,
  44. ...agent ? { agent } : {},
  45. ...over.signal ? { signal: over.signal } : {},
  46. })
  47. }
  48. /** Join text blocks from one rendered tool result. */
  49. export function text(result: { content: { type: string; text?: string }[] }): string {
  50. return result.content.filter(block => block.type === 'text').map(block => block.text).join('')
  51. }