Skip to content
中文

Compatible Provider and custom Providers ​

Support owns the common Chat Completions HTTP/SSE lifecycle; an App can substitute its own ChatProvider.

CompatibleModelProvider accepts {config,ports?,observe?,errors?}, and config can be a static configuration or a dynamic read function. Static configuration and callbacks are fixed, while dynamic factories read the current configuration for each actual call. Request keys come from server-side configuration and are not written into the frontend, articles, or the code repository.

requestBody allows provider extension parameters but must not override Core-controlled fields such as model/messages/stream/max_tokens/max_completion_tokens. For a different request protocol, implement ChatProvider rather than bypassing the constraints of the same adapter.

Completion determination is selected through ports.completionPolicy. The default comes from completionPolicyCatalog.default, and whether function fragments are accepted, the expected finish_reason, and completeness before [DONE] are all decided by whether the call declares functions. completionPolicyCatalog.session decides by the tool_choice on the wire: auto requires all started calls to be complete and permits body text, required and an explicit function object require at least one complete call, none rejects any call fragment, an omitted field uses the default completion rules, and unknown values are rejected before the request is sent. A policy only decides whether a response satisfies the request; it does not take over the request loop, transport protection, or tool execution.

createToolCallCollector aggregates call fragments by index, and policies and direct callers share the same implementation; ID conflicts, duplicate IDs across indices, and incomplete calls are all rejected as protocol errors. The protocol layer never restricts receiving multiple call fragments, and parallel_tool_calls is only a field that is sent; the single-proposal limit on the Agent path is decided by nativeFunctionInteraction, while direct Provider use is not subject to that limit and does not thereby gain tool execution or scheduling capability. Use createSessionModelTransport explicitly when prose and multiple calls are required; it reuses the same aggregation collector without changing the nativeFunctionInteraction single-proposal contract.

ProviderMessage provides the tool results and assistant toolCalls needed for a direct connection; chatCompletionMessages explicitly maps them to tool_call_id and nested tool_calls, and rejects mismatched roles and fields. Only declared fields are sent, and extra fields, including raw wire fields, are not forwarded; toolCallId must be a non-empty string, and role and content are validated per the message contract. Core ModelMessage now supports assistant toolCalls and role:'tool' fields; the ChatProvider abstract signature stays readonly ModelMessage[].

Tool registration, business argument validation, authorization, and execution belong to the host. createToolCallCollector.complete() checks the completeness of call IDs and names; it does not parse or validate business argument JSON.

A Provider must finish per the transport contract: incomplete, over limit, format mismatch, cancellation, and provider errors fail separately. Error mapping is not an automatic retry policy. chatCompletionMessages/Parts/FunctionOptions are wire-boundary conversions, not business protocol validators.

Progressive examples for the same business case ​

Connect a common protocol with real server-side configuration ​

Beginner · Node.js

Running this example requires connection configuration for an available model service.

ts
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);
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);

Declared fields and wire mapping for a direct tool thread ​

Configuration · Node.js

This only demonstrates message conversion, with no network request or tool execution. Raw tool_calls, tool_call_id, and host-attached data are not forwarded.

ts
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));
js
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));

Connect an offline object implementation to the same contract ​

Configuration · Node.js

ts
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);
js
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);

Implement the Core Provider contract with a class ​

Extension · Node.js

ts
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);
js
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);

Policies & implementations ​

API reference, usage examples and developer guides.