diff --git a/packages/sdk/server-ai/__tests__/RunnerProtocol.test.ts b/packages/sdk/server-ai/__tests__/RunnerProtocol.test.ts new file mode 100644 index 0000000000..70c1e11aa7 --- /dev/null +++ b/packages/sdk/server-ai/__tests__/RunnerProtocol.test.ts @@ -0,0 +1,72 @@ +import type { AgentGraphRunnerResult } from '../src/api/graph/types'; +import type { RunnerResult } from '../src/api/model/types'; +import type { AgentGraphRunner, Runner } from '../src/api/providers/Runner'; + +/** + * Verify that the Runner and AgentGraphRunner protocols can be implemented + * by a plain object (no abstract class required). + */ +describe('Runner protocol', () => { + it('can be implemented as a plain object (no class extension required)', async () => { + const runnerResult: RunnerResult = { + content: 'Hello from runner', + metrics: { success: true }, + }; + + const myRunner: Runner = { + run: jest.fn().mockResolvedValue(runnerResult), + }; + + const result = await myRunner.run('Hello'); + + expect(result.content).toBe('Hello from runner'); + expect(result.metrics.success).toBe(true); + }); + + it('Runner.run() accepts optional outputType for structured output', async () => { + const runnerResult: RunnerResult = { + content: '', + metrics: { success: true }, + parsed: { score: 0.9, reasoning: 'good' }, + }; + + const myRunner: Runner = { + run: jest.fn().mockResolvedValue(runnerResult), + }; + + const schema = { type: 'object', properties: { score: { type: 'number' } } }; + const result = await myRunner.run('Evaluate', schema); + + expect(result.parsed).toEqual({ score: 0.9, reasoning: 'good' }); + expect(myRunner.run).toHaveBeenCalledWith('Evaluate', schema); + }); + + it('AgentGraphRunner can be implemented as a plain object', async () => { + const graphResult: AgentGraphRunnerResult = { + content: 'Graph output', + metrics: { + success: true, + path: ['node-a'], + nodeMetrics: { 'node-a': { success: true } }, + }, + }; + + const myGraphRunner: AgentGraphRunner = { + run: jest.fn().mockResolvedValue(graphResult), + }; + + const result = await myGraphRunner.run('user input'); + + expect(result.content).toBe('Graph output'); + expect(result.metrics.path).toEqual(['node-a']); + }); + + it('RunnerResult does not include evaluations field', () => { + const result: RunnerResult = { + content: 'test', + metrics: { success: true }, + }; + + expect('evaluations' in result).toBe(false); + }); +});