Presentation
KIT · constant · 运行时值
导入
import { Presentation } from '@jangts/agent-core/kits/presentation';import { Presentation } from '@jangts/agent-core/kits/presentation';其他公开入口:@jangts/agent-core/types、@jangts/agent-core/kits/presentation、@jangts/agent-core/kits。
定义
字段结构
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
replyFormatChecks | Readonly<{ bubbles(totalLimit: number): ReplyFormatCheck; textBudget(maxChars: number): ReplyFormatCheck; }> | 必需 | 见类型定义及调用场景。 |
REPLY_BUBBLE_BOUNDARY | "\u001E" | 必需 | 见类型定义及调用场景。 |
成员目录
| 成员 | 用途 |
|---|---|
defineOutputCatalog | 完整输入输出见下方重载。 |
defineReplyFormats | 完整输入输出见下方重载。 |
mergeReplyBubbles | 完整输入输出见下方重载。 |
defineTextTemplate | 完整输入输出见下方重载。 |
defineSemanticSlots | 完整输入输出见下方重载。 |
selectVariant | 完整输入输出见下方重载。 |
resolvePreference | 完整输入输出见下方重载。 |
replyBubbleTexts | 完整输入输出见下方重载。 |
publishReplyEvents | 完整输入输出见下方重载。 |
serializeOutputEvents | 完整输入输出见下方重载。 |
textOutputEvents | 完整输入输出见下方重载。 |
成员详情
defineOutputCatalog
Presentation.defineOutputCatalog<App = unknown>(definitions: readonly OutputDefinition[], options?: OutputCatalogOptions<App>): Readonly<{ toModelSchema: (id: string) => { type: string; properties: { [k: string]: Readonly<Record<string, unknown>>; }; required: string[]; additionalProperties: boolean; }; check(request: { outputId: string; value: unknown; }): { status: "passed" | "failed"; value: Record<string, unknown>; issues: OutputIssue[]; }; compose(request: { outputId: string; input: OutputComposeInput; }, context: OperationContext<App>): OutputResult; definitions: readonly Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>[]; ids(): readonly string[]; values(): IterableIterator<Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>>; entries(): IterableIterator<readonly [string, Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>]>; has(id: string): boolean; get(id: string): Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }> | undefined; require(id: string): Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>; }>Compile output definitions into one catalog.
Compilation is where most mistakes surface: group membership is checked for cycles, master templates must have exactly one slot per field, and every bound/semantic field must name a registered provider. Composition then trusts the compiled catalog.
| 类型参数 | 约束 | 默认类型 |
|---|---|---|
App | — | unknown |
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
definitions | readonly OutputDefinition[] | 必需 | — |
options | OutputCatalogOptions<App> | 可省略 | — |
options 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
providers | readonly OutputProvider<App>[] | undefined | 可省略 | 只读。Providers referenced by bound and semantic fields. |
observe | ((result: OutputResult) => void | Promise<void>) | undefined | 可省略 | 只读。Observer invoked with every composed result. |
返回: Readonly<{ toModelSchema: (id: string) => { type: string; properties: { [k: string]: Readonly<Record<string, unknown>>; }; required: string[]; additionalProperties: boolean; }; check(request: { outputId: string; value: unknown; }): { status: "passed" \| "failed"; value: Record<string, unknown>; issues: OutputIssue[]; }; compose(request: { outputId: string; input: OutputComposeInput; }, context: OperationContext<App>): OutputResult; definitions: readonly Readonly<{ id: string; kind: "text" \| "master" \| "record" \| "group"; fields?: readonly OutputField[] \| undefined; master?: string \| undefined; allowedOutputIds?: readonly string[] \| undefined; maxItems?: number \| undefined; }>[]; ids(): readonly string[]; values(): IterableIterator<Readonly<{ id: string; kind: "text" \| "master" \| "record" \| "group"; fields?: readonly OutputField[] \| undefined; master?: string \| undefined; allowedOutputIds?: readonly string[] \| undefined; maxItems?: number \| undefined; }>>; entries(): IterableIterator<readonly [string, Readonly<{ id: string; kind: "text" \| "master" \| "record" \| "group"; fields?: readonly OutputField[] \| undefined; master?: string \| undefined; allowedOutputIds?: readonly string[] \| undefined; maxItems?: number \| undefined; }>]>; has(id: string): boolean; get(id: string): Readonly<{ id: string; kind: "text" \| "master" \| "record" \| "group"; fields?: readonly OutputField[] \| undefined; master?: string \| undefined; allowedOutputIds?: readonly string[] \| undefined; maxItems?: number \| undefined; }> \| undefined; require(id: string): Readonly<{ id: string; kind: "text" \| "master" \| "record" \| "group"; fields?: readonly OutputField[] \| undefined; master?: string \| undefined; allowedOutputIds?: readonly string[] \| undefined; maxItems?: number \| undefined; }>; }>。
返回值结构
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
toModelSchema | (id: string) => { type: string; properties: { [k: string]: Readonly<Record<string, unknown>>; }; required: string[]; additionalProperties: boolean; } | 必需 | JSON schema of the fields a model must generate for this output. |
check | (request: { outputId: string; value: unknown; }) => { status: "passed" | "failed"; value: Record<string, unknown>; issues: OutputIssue[]; } | 必需 | Validate a generated value without composing it. |
compose | (request: { outputId: string; input: OutputComposeInput; }, context: OperationContext<App>) => OutputResult | 必需 | Compose the final output value from generated fields, providers and constants. |
definitions | readonly Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>[] | 必需 | 只读。Definitions in registration order; each entry is frozen by the implementation. |
ids | () => readonly string[] | 必需 | Ids in the same order as definitions. |
values | () => IterableIterator<Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>> | 必需 | 见类型定义及调用场景。 |
entries | () => IterableIterator<readonly [string, Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }>]> | 必需 | 见类型定义及调用场景。 |
has | (id: string) => boolean | 必需 | 见类型定义及调用场景。 |
get | (id: string) => Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }> | undefined | 必需 | Returns the definition or undefined; the caller decides whether absence is an error. |
require | (id: string) => Readonly<{ id: string; kind: "text" | "master" | "record" | "group"; fields?: readonly OutputField[] | undefined; master?: string | undefined; allowedOutputIds?: readonly string[] | undefined; maxItems?: number | undefined; }> | 必需 | Returns the definition or throws; use when the id is a required reference. |
defineReplyFormats
Presentation.defineReplyFormats<Context = unknown>(options: ReplyFormatOptions<Context>): Readonly<{ resolve(context: Context): CompiledReplyFormat | undefined; }>Compile reply formats so that checks run only for templates the current turn uses.
| 类型参数 | 约束 | 默认类型 |
|---|---|---|
Context | — | unknown |
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
options | ReplyFormatOptions<Context> | 必需 | — |
options 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
formats | readonly ReplyFormatDefinition<Context>[] | 必需 | 只读。见类型定义及调用场景。 |
checks | readonly ReplyFormatCheck[] | 必需 | 只读。见类型定义及调用场景。 |
返回: Readonly<{ resolve(context: Context): CompiledReplyFormat \| undefined; }>。
返回值结构
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
resolve | (context: Context) => CompiledReplyFormat | undefined | 必需 | The template this turn uses, or undefined when it has no output-format requirement. |
mergeReplyBubbles
Presentation.mergeReplyBubbles(bubbles: readonly string[], limit: number): string[]Normalize a reply that has no declared format: drop blank bubbles and merge overflow into the last allowed one. Text content is preserved.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
bubbles | readonly string[] | 必需 | — |
limit | number | 必需 | — |
返回: string[]。
defineTextTemplate
Presentation.defineTextTemplate(master: string, options?: TextTemplateOptions): Readonly<{ slots: readonly string[]; render(values: Readonly<Record<string, string>>): string; }>The program owns the master; inserted model text is a value and is substituted once only.
Substitution is single-pass String.replace: a value that itself contains {{slot}} is left as literal text, so a slot value cannot inject further substitutions.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
master | string | 必需 | — |
options | TextTemplateOptions | 可省略 | — |
options 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
observe | ((result: string) => void | Promise<void>) | undefined | 可省略 | 只读。Observer invoked with every rendered text. |
errors | { readonly missingField?: (key: string) => Error | string; } | undefined | 可省略 | 只读。Builds the error for a slot the caller did not supply; receives the slot name. |
返回: Readonly<{ slots: readonly string[]; render(values: Readonly<Record<string, string>>): string; }>。
返回值结构
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
slots | readonly string[] | 必需 | 见类型定义及调用场景。 |
render | (values: Readonly<Record<string, string>>) => string | 必需 | 见类型定义及调用场景。 |
defineSemanticSlots
Presentation.defineSemanticSlots(definition: SemanticSlotDefinition): Readonly<{ namespace: string; isValid(text: string): boolean; }>Models select public semantic keys; hosts retain private values and selection policy.
A text is valid when it uses at most maxOccurrences namespaced slots, every name is a declared key, and no malformed {{namespace remnant is left behind — the last check stops partial or misspelled placeholders from reaching the template engine.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
definition | SemanticSlotDefinition | 必需 | — |
definition 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
namespace | string | 必需 | 只读。Slot namespace; must match ^[a-z][a-z0-9-]*$. |
keys | readonly string[] | 必需 | 只读。Allowed keys after the namespace; any other key makes the text invalid. |
maxOccurrences | number | 必需 | 只读。Maximum total occurrences across the text; must be a non-negative integer. |
返回: Readonly<{ namespace: string; isValid(text: string): boolean; }>。
返回值结构
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
namespace | string | 必需 | 见类型定义及调用场景。 |
isValid | (text: string) => boolean | 必需 | 见类型定义及调用场景。 |
selectVariant
Presentation.selectVariant<Value>(values: readonly Value[], options: { previous?: Value; randomIndex(limit: number): number; }): Value | undefinedPick one variant, avoiding an immediate repeat when alternatives exist.
When every candidate equals previous, the full list is used again, so a single-value set still yields a result instead of failing.
| 类型参数 | 约束 | 默认类型 |
|---|---|---|
Value | — | — |
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
values | readonly Value[] | 必需 | — |
options | { previous?: Value; randomIndex(limit: number): number; } | 必需 | — |
options 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
previous | Value | undefined | 可省略 | 见类型定义及调用场景。 |
randomIndex | (limit: number) => number | 必需 | 见类型定义及调用场景。 |
返回: Value \| undefined。
等待后的值: Awaited<Value> \| undefined。
resolvePreference
Presentation.resolvePreference<Value>(candidates: readonly { value: Value; priority: number; order: number; }[]): Value | undefinedResolve the preferred candidate.
Highest priority wins; order breaks ties with the later entry first, so recency can be encoded without touching priority. The input array is copied before sorting.
| 类型参数 | 约束 | 默认类型 |
|---|---|---|
Value | — | — |
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
candidates | readonly { value: Value; priority: number; order: number; }[] | 必需 | — |
返回: Value \| undefined。
等待后的值: Awaited<Value> \| undefined。
replyBubbleTexts
Presentation.replyBubbleTexts(reply: ReplyBubbleInput): string[]Collect the non-empty-text parts of a reply.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
reply | ReplyBubbleInput | 必需 | — |
reply 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
text | unknown | 可省略 | Primary reply text. |
bubbles | unknown | 可省略 | Additional bubbles appended after text, when the value is an array. |
返回: string[]。
publishReplyEvents
Presentation.publishReplyEvents(request: { text: string; delivery?: ReplyDeliveryOptions; }, context: ExecutionContext): AsyncIterable<AgentOutputEvent>Stream one reply as paced text chunks and bubble separators.
Publish a validated reply without exposing its internal boundary to consumers. The pacing options are upper bounds, not exact timings: the pause and interval are clamped against maxDurationMs, so added delay never exceeds the budget while every chunk is still emitted.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
request | { text: string; delivery?: ReplyDeliveryOptions; } | 必需 | — |
context | ExecutionContext | 必需 | — |
request 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
text | string | 必需 | 见类型定义及调用场景。 |
delivery | ReplyDeliveryOptions | undefined | 可省略 | 见类型定义及调用场景。 |
context 的字段
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
logger | SubFlowLog | undefined | 可省略 | 只读。Explicit optional recorder for this operation; never inferred from diagnostics. |
signal | AbortSignal | 必需 | 只读。Non-optional cancellation signal; never undefined once execution has started. |
diagnostics | DiagnosticContext | undefined | 可省略 | 只读。Diagnostic scope of the enclosing call, when recording is enabled. |
返回: AsyncIterable<AgentOutputEvent>。
serializeOutputEvents
Presentation.serializeOutputEvents(events: AsyncIterable<AgentOutputEvent>): AsyncIterable<string>Serialize the event protocol for callers using a text stream.
preview events are dropped because they are not final text; bubbles become the boundary character again, so a consumer can re-split the stream.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
events | AsyncIterable<AgentOutputEvent> | 必需 | — |
返回: AsyncIterable<string>。
textOutputEvents
Presentation.textOutputEvents(chunks: AsyncIterable<string>): AsyncIterable<AgentOutputEvent>Wrap already-chunked text as text events.
| 参数 | 类型 | 传递 | 说明 |
|---|---|---|---|
chunks | AsyncIterable<string> | 必需 | — |
返回: AsyncIterable<AgentOutputEvent>。
使用示例
- Presentation:字段所有权与最终输出:模型只填写被分配的字段;固定文字、绑定值和语义键由程序拥有。
先组合一段生成文本
入门 · Node.js
import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog([
{ id: 'reply', kind: 'text', fields: [{ id: 'text', owner: 'generated' }] },
]);
console.log(outputs.toModelSchema('reply'));
const output = outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可定义节点。' } } }, { app: {}, signal: new AbortController().signal });
console.log(output.value);import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog([
{ id: 'reply', kind: 'text', fields: [{ id: 'text', owner: 'generated' }] },
]);
console.log(outputs.toModelSchema('reply'));
const output = outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可定义节点。' } } }, { app: {}, signal: new AbortController().signal });
console.log(output.value);相同 compose 让程序持有说明前缀
配置 · Node.js
import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog([
{ id: 'reply', kind: 'master', master: '{{prefix}}{{text}}', fields: [
{ id: 'prefix', owner: 'constant', constantValue: '接口说明:' },
{ id: 'text', owner: 'generated' },
] },
]);
console.log(outputs.toModelSchema('reply')); // 只有 text 由模型填写
console.log(outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可定义节点。' } } }, { app: {}, signal: new AbortController().signal }).value);import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog([
{ id: 'reply', kind: 'master', master: '{{prefix}}{{text}}', fields: [
{ id: 'prefix', owner: 'constant', constantValue: '接口说明:' },
{ id: 'text', owner: 'generated' },
] },
]);
console.log(outputs.toModelSchema('reply')); // 只有 text 由模型填写
console.log(outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可定义节点。' } } }, { app: {}, signal: new AbortController().signal }).value);增加绑定字段和语义键,保持 compose 入口
扩展 · Node.js
import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog<{ displayName: string }>([
{ id: 'reply', kind: 'record', fields: [
{ id: 'text', owner: 'generated' },
{ id: 'author', owner: 'bound', providerId: 'author' },
{ id: 'tone', owner: 'semantic', providerId: 'tone', semanticKeys: ['brief', 'detailed'] },
] },
], { providers: [
{ id: 'author', resolve: (_input, { app }) => app.displayName },
{ id: 'tone', resolve: ({ key }) => key === 'brief' ? '简洁说明' : '详细说明' },
] });
const result = outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可。', tone: 'brief' } } }, { app: { displayName: '示例助手' }, signal: new AbortController().signal });
console.log(result.value);import { Presentation } from '@jangts/agent-core/kits/presentation';
const outputs = Presentation.defineOutputCatalog([
{ id: 'reply', kind: 'record', fields: [
{ id: 'text', owner: 'generated' },
{ id: 'author', owner: 'bound', providerId: 'author' },
{ id: 'tone', owner: 'semantic', providerId: 'tone', semanticKeys: ['brief', 'detailed'] },
] },
], { providers: [
{ id: 'author', resolve: (_input, { app }) => app.displayName },
{ id: 'tone', resolve: ({ key }) => key === 'brief' ? '简洁说明' : '详细说明' },
] });
const result = outputs.compose({ outputId: 'reply', input: { generated: { text: '普通对象即可。', tone: 'brief' } } }, { app: { displayName: '示例助手' }, signal: new AbortController().signal });
console.log(result.value);相关策略源码
相关定义
AgentOutputEventdefineOutputCatalogdefineReplyFormatsdefineSemanticSlotsdefineTextTemplateExecutionContextmergeReplyBubblesOutputCatalogOptionsOutputDefinitionpublishReplyEventsReplyBubbleInputreplyBubbleTextsReplyFormatOptionsresolvePreferenceselectVariantSemanticSlotDefinitionserializeOutputEventstextOutputEventsTextTemplateOptions