兼容 Provider 与自定义 Provider
Support 负责常见 Chat Completions HTTP/SSE 生命周期;App 可替换为自己的 ChatProvider。
CompatibleModelProvider 接受 {config,ports?,observe?,errors?},config 可是静态配置或动态读取函数。静态配置及回调固定,动态工厂按实际调用取当前配置。请求密钥来自服务端配置,不写在前端、文章或代码仓库。
requestBody 允许供应商扩展参数,但不能覆盖 model/messages/stream/max_tokens/max_completion_tokens 等 Core 已控制字段。不同请求协议请实现 ChatProvider,而不是绕过同一适配器的约束。
完成判定通过 ports.completionPolicy 选择。缺省使用 completionPolicyCatalog.default,是否接受函数分片、期望的 finish_reason、[DONE] 前的完整性都按调用是否声明 functions 判定。completionPolicyCatalog.session 按线上 tool_choice 判定:auto 要求已开始的调用全部完整并允许正文,required 与指定函数对象要求至少一个完整调用,none 拒绝任何调用分片,字段省略采用 default 的完成规则,未知值在发请求前拒绝。策略只判定响应是否满足请求,不接管请求循环、传输保护或工具执行。
createToolCallCollector 按 index 聚合调用分片,策略与直接调用方共用同一实现;ID 冲突、跨 index 重复 ID 与残缺调用都按协议错误拒绝。协议层从不限制接收多个调用分片,parallel_tool_calls 只是发送字段;Agent 路径的单提案限制由 nativeFunctionInteraction 决定,直接使用 Provider 不受该限制,也不因此获得工具执行或调度能力。需要正文 + 多调用时显式使用 createSessionModelTransport;它复用同一聚合 collector,但不改变 nativeFunctionInteraction 的单提案合同。
ProviderMessage 提供直连所需的 tool 结果与 assistant toolCalls;chatCompletionMessages 显式映射为 tool_call_id 与嵌套 tool_calls,角色与字段不匹配时拒绝。只发送声明字段,额外字段(包括原始 wire 字段)不透传;toolCallId 必须是非空字符串,role 与 content 按消息合同校验。Core ModelMessage 现支持 assistant toolCalls 与 role:'tool' 字段;ChatProvider 抽象签名保持 readonly ModelMessage[] 不变。
工具的注册、参数业务校验、授权和执行由宿主负责。createToolCallCollector.complete() 检查调用 ID 与名称的完整性,不解析或验证业务参数 JSON。
Provider 必须按传输合同结束:未完成、超限、格式不符、取消与供应商错误分别失败。错误映射不等于自动重试策略。chatCompletionMessages/Parts/FunctionOptions 是 wire 边界转换,不是业务协议验证器。
由简到繁的同业务示例
真实服务端配置接入常见协议
入门 · Node.js
运行此例需要提供可用模型服务的连接配置。
import { ModelRunner, Support } from '@jangts/agent-core';
const provider = new Support.CompatibleModelProvider({ config: {
baseUrl: process.env.MODEL_BASE_URL!, apiKey: process.env.MODEL_API_KEY!, model: process.env.MODEL_NAME!,
} });
const runner = new ModelRunner({ provider });
console.log((await runner.invoke('你好')).value);import { ModelRunner, Support } from '@jangts/agent-core';
const provider = new Support.CompatibleModelProvider({ config: {
baseUrl: process.env.MODEL_BASE_URL, apiKey: process.env.MODEL_API_KEY, model: process.env.MODEL_NAME,
} });
const runner = new ModelRunner({ provider });
console.log((await runner.invoke('你好')).value);直连工具线程的声明字段与 wire 映射
配置 · Node.js
只演示消息转换,无网络请求或工具执行。原始 tool_calls、tool_call_id 和宿主附加数据不透传。
import { Support } from '@jangts/agent-core';
const messages: Support.ProviderMessage[] = [
{ role: 'user', content: '查询资料' },
{ role: 'assistant', content: '', toolCalls: [
{ id: 'call-1', name: 'lookup', arguments: '{"query":"资料"}' },
] },
{ role: 'tool', content: '{"found":true}', toolCallId: 'call-1' },
];
console.log(Support.chatCompletionMessages(messages));import { Support } from '@jangts/agent-core';
const messages = [
{ role: 'user', content: '查询资料' },
{ role: 'assistant', content: '', toolCalls: [
{ id: 'call-1', name: 'lookup', arguments: '{"query":"资料"}' },
] },
{ role: 'tool', content: '{"found":true}', toolCallId: 'call-1' },
];
console.log(Support.chatCompletionMessages(messages));同一合同接入离线对象实现
配置 · Node.js
import { ModelRunner } from '@jangts/agent-core/model';
const provider = { async *stream(messages: readonly { content: string }[]) {
yield '离线回答:' + messages.at(-1)!.content;
} };
console.log((await new ModelRunner({ provider }).invoke('你好')).value);import { ModelRunner } from '@jangts/agent-core/model';
const provider = { async *stream(messages) {
yield '离线回答:' + messages.at(-1).content;
} };
console.log((await new ModelRunner({ provider }).invoke('你好')).value);由类实现 Core 的 Provider 合同
扩展 · Node.js
import { ChatProvider, ModelRunner } from '@jangts/agent-core';
import type { ModelMessage, ModelProviderContext } from '@jangts/agent-core/types';
class LocalProvider extends ChatProvider {
async *stream(messages: readonly ModelMessage[], context: ModelProviderContext) {
context.signal.throwIfAborted();
yield '离线回答:' + messages.at(-1)!.content;
}
}
console.log((await new ModelRunner({ provider: new LocalProvider() }).invoke('你好')).value);import { ChatProvider, ModelRunner } from '@jangts/agent-core';
class LocalProvider extends ChatProvider {
async *stream(messages, context) {
context.signal.throwIfAborted();
yield '离线回答:' + messages.at(-1).content;
}
}
console.log((await new ModelRunner({ provider: new LocalProvider() }).invoke('你好')).value);