Actual request measurement and same-call preparation snapshot
Use prepare only when context must be handled ahead of time; multiple measurements and the final send reuse the same transport preparation.
An ordinary invoke does not require an added prepare step. prepare fixes the dynamic transport instructions, Schema, options, and collector factory for that call; measure allows checking candidates multiple times without consuming model call counts.
measure includes the actual body, transport instructions, and declared measurements, and shows messageChars, instructionChars, schemaChars, transportChars, and inputChars separately. An input token limit exists only after an explicit counter is injected; it already deducts the output reservation and safety margin for this call.
A preparation handle belongs to the Runner/Workflow request that created it and can only be finally consumed once; the transport, stageId, or output reservation cannot be changed. Measurement is not a budget reservation or an admission ticket: checks are repeated before the final Provider call. Cancellation, expiry, foreign handles, and repeated consumption are all rejected.
Progressive examples for the same business case
Ordinary calls still only use invoke
Beginner · Node.js
import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages: readonly { content: string }[]) {
yield '收到:' + messages.at(-1)!.content;
},
};
const runner = new ModelRunner({ provider });
console.log((await runner.invoke('你好')).value);import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages) {
yield '收到:' + messages.at(-1).content;
},
};
const runner = new ModelRunner({ provider });
console.log((await runner.invoke('你好')).value);Prepare once only when measurement is needed
Configuration · Node.js
import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages: readonly { content: string }[]) {
yield '收到:' + messages.at(-1)!.content;
},
};
const runner = new ModelRunner({ provider, maxInputChars: 1000 });
const prepared = runner.prepare({ stageId: 'reply', maxOutputTokens: 400 });
const input = '你好,解释这次服务状态';
console.log(prepared.measure(input));
const result = await runner.invoke({ input, prepared });
console.log(result.value);import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages) {
yield '收到:' + messages.at(-1).content;
},
};
const runner = new ModelRunner({ provider, maxInputChars: 1000 });
const prepared = runner.prepare({ stageId: 'reply', maxOutputTokens: 400 });
const input = '你好,解释这次服务状态';
console.log(prepared.measure(input));
const result = await runner.invoke({ input, prepared });
console.log(result.value);Check multiple candidates with the same preparation handle
Extension · Node.js
This only demonstrates the App deciding candidates; trimming real context must still preserve the original question, authorization, and evidence integrity.
import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages: readonly { content: string }[]) {
yield '收到:' + messages.at(-1)!.content;
},
};
const runner = new ModelRunner({ provider, maxInputChars: 1000 });
const prepared = runner.prepare({ stageId: 'reply' });
const withHistory = [{ role: 'user' as const, content: '历史:上次正常。\n本轮:现在呢?' }];
const currentOnly = [{ role: 'user' as const, content: '现在的服务状态?' }];
const selected = prepared.measure(withHistory).withinBudget ? withHistory : currentOnly;
if (!prepared.measure(selected).withinBudget) throw new Error('输入仍超限');
console.log((await runner.invoke({ input: selected, prepared })).value);import { ModelRunner } from '@jangts/agent-core/model';
const provider = {
async *stream(messages) {
yield '收到:' + messages.at(-1).content;
},
};
const runner = new ModelRunner({ provider, maxInputChars: 1000 });
const prepared = runner.prepare({ stageId: 'reply' });
const withHistory = [{ role: 'user', content: '历史:上次正常。\n本轮:现在呢?' }];
const currentOnly = [{ role: 'user', content: '现在的服务状态?' }];
const selected = prepared.measure(withHistory).withinBudget ? withHistory : currentOnly;
if (!prepared.measure(selected).withinBudget)
throw new Error('输入仍超限');
console.log((await runner.invoke({ input: selected, prepared })).value);