Skip to content

Commit 635cf16

Browse files
jsonbaileyclaude
andcommitted
feat: add Runner and AgentGraphRunner protocols; deprecate AIProvider (AIC-2388)
Introduces Runner interface (run(input, outputType?) => Promise<RunnerResult>) as the new provider protocol replacing the AIProvider abstract class. Introduces AgentGraphRunner interface (run(input) => Promise<AgentGraphRunnerResult>) for graph-level providers. Confirms RunnerResult shape (content, metrics, raw?, parsed? — no evaluations). Deprecates AIProvider abstract class with JSDoc notice directing implementors to the Runner interface. Exports Runner and AgentGraphRunner from public API. This unblocks the js-providers agent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2ab8546 commit 635cf16

4 files changed

Lines changed: 119 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { AgentGraphRunnerResult } from '../src/api/graph/types';
2+
import type { RunnerResult } from '../src/api/model/types';
3+
import type { AgentGraphRunner, Runner } from '../src/api/providers/Runner';
4+
5+
/**
6+
* Verify that the Runner and AgentGraphRunner protocols can be implemented
7+
* by a plain object (no abstract class required).
8+
*/
9+
describe('Runner protocol', () => {
10+
it('can be implemented as a plain object (no class extension required)', async () => {
11+
const runnerResult: RunnerResult = {
12+
content: 'Hello from runner',
13+
metrics: { success: true },
14+
};
15+
16+
const myRunner: Runner = {
17+
run: jest.fn().mockResolvedValue(runnerResult),
18+
};
19+
20+
const result = await myRunner.run([{ role: 'user', content: 'Hello' }]);
21+
22+
expect(result.content).toBe('Hello from runner');
23+
expect(result.metrics.success).toBe(true);
24+
});
25+
26+
it('Runner.run() accepts optional outputType for structured output', async () => {
27+
const runnerResult: RunnerResult = {
28+
content: '',
29+
metrics: { success: true },
30+
parsed: { score: 0.9, reasoning: 'good' },
31+
};
32+
33+
const myRunner: Runner = {
34+
run: jest.fn().mockResolvedValue(runnerResult),
35+
};
36+
37+
const schema = { type: 'object', properties: { score: { type: 'number' } } };
38+
const result = await myRunner.run([{ role: 'user', content: 'Evaluate' }], schema);
39+
40+
expect(result.parsed).toEqual({ score: 0.9, reasoning: 'good' });
41+
expect(myRunner.run).toHaveBeenCalledWith([{ role: 'user', content: 'Evaluate' }], schema);
42+
});
43+
44+
it('AgentGraphRunner can be implemented as a plain object', async () => {
45+
const graphResult: AgentGraphRunnerResult = {
46+
content: 'Graph output',
47+
metrics: {
48+
success: true,
49+
path: ['node-a'],
50+
nodeMetrics: { 'node-a': { success: true } },
51+
},
52+
};
53+
54+
const myGraphRunner: AgentGraphRunner = {
55+
run: jest.fn().mockResolvedValue(graphResult),
56+
};
57+
58+
const result = await myGraphRunner.run('user input');
59+
60+
expect(result.content).toBe('Graph output');
61+
expect(result.metrics.path).toEqual(['node-a']);
62+
});
63+
64+
it('RunnerResult does NOT include evaluations field', () => {
65+
const result: RunnerResult = {
66+
content: 'test',
67+
metrics: { success: true },
68+
};
69+
70+
// TypeScript would catch this at compile time, but verify at runtime shape too
71+
expect('evaluations' in result).toBe(false);
72+
});
73+
});

packages/sdk/server-ai/src/api/providers/AIProvider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { StructuredResponse } from '../judge/types';
1111
*
1212
* Following the AICHAT spec recommendation to use base classes with non-abstract methods
1313
* for better extensibility and backwards compatibility.
14+
*
15+
* @deprecated Use the `Runner` interface instead. Provider implementations should
16+
* implement `Runner` (and optionally `AgentGraphRunner`) rather than extending this
17+
* abstract class. This class will be removed in a future major version.
1418
*/
1519
export abstract class AIProvider {
1620
protected readonly logger?: LDLogger;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { LDMessage } from '../config/types';
2+
import { AgentGraphRunnerResult } from '../graph/types';
3+
import { RunnerResult } from '../model/types';
4+
5+
/**
6+
* Runner protocol for AI model providers.
7+
*
8+
* Providers implementing the Runner interface can be used with ManagedModel
9+
* and ManagedAgent without extending the deprecated AIProvider abstract class.
10+
*
11+
* A single Runner interface covers both chat (completion) and agent use cases.
12+
* For structured output (e.g., judge evaluation), pass an `outputType` schema
13+
* and access the parsed result via `RunnerResult.parsed`.
14+
*/
15+
export interface Runner {
16+
/**
17+
* Invoke the model with an array of messages.
18+
*
19+
* @param input Array of LDMessage objects representing the conversation or prompt.
20+
* @param outputType Optional JSON schema for structured output. When provided,
21+
* the model should return structured data accessible via `RunnerResult.parsed`.
22+
* @returns Promise resolving to a RunnerResult.
23+
*/
24+
run(input: LDMessage[], outputType?: Record<string, unknown>): Promise<RunnerResult>;
25+
}
26+
27+
/**
28+
* Runner protocol for agent graph providers.
29+
*
30+
* Providers implementing AgentGraphRunner can execute an entire agent graph
31+
* and return a structured AgentGraphRunnerResult.
32+
*/
33+
export interface AgentGraphRunner {
34+
/**
35+
* Execute the agent graph with the given input.
36+
*
37+
* @param input The user input to process through the graph.
38+
* @returns Promise resolving to an AgentGraphRunnerResult.
39+
*/
40+
run(input: string): Promise<AgentGraphRunnerResult>;
41+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './AIProvider';
22
export * from './AIProviderFactory';
3+
export type { Runner, AgentGraphRunner } from './Runner';

0 commit comments

Comments
 (0)