Presentation
KIT · constant · Runtime value
Import
import { Presentation } from '@jangts/agent-core/kits/presentation';import { Presentation } from '@jangts/agent-core/kits/presentation';Other public entry points:@jangts/agent-core/types, @jangts/agent-core/kits/presentation, @jangts/agent-core/kits.
Definition
Field structure
| Field | Type | Required | Description |
|---|---|---|---|
replyFormatChecks | Readonly<{ bubbles(totalLimit: number): ReplyFormatCheck; textBudget(maxChars: number): ReplyFormatCheck; }> | Required | See the type definition and usage context. |
REPLY_BUBBLE_BOUNDARY | "\u001E" | Required | See the type definition and usage context. |
Member index
| Member | Purpose |
|---|---|
defineOutputCatalog | See the overload below for full inputs and outputs. |
defineReplyFormats | See the overload below for full inputs and outputs. |
mergeReplyBubbles | See the overload below for full inputs and outputs. |
defineTextTemplate | See the overload below for full inputs and outputs. |
defineSemanticSlots | See the overload below for full inputs and outputs. |
selectVariant | See the overload below for full inputs and outputs. |
resolvePreference | See the overload below for full inputs and outputs. |
replyBubbleTexts | See the overload below for full inputs and outputs. |
publishReplyEvents | See the overload below for full inputs and outputs. |
serializeOutputEvents | See the overload below for full inputs and outputs. |
textOutputEvents | See the overload below for full inputs and outputs. |
Member details
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.
| Type parameter | Constraint | Default type |
|---|---|---|
App | — | unknown |
| Parameter | Type | Passing | Description |
|---|---|---|---|
definitions | readonly OutputDefinition[] | Required | — |
options | OutputCatalogOptions<App> | Optional | — |
Fields of options
| Field | Type | Required | Description |
|---|---|---|---|
providers | readonly OutputProvider<App>[] | undefined | Optional | Read-only. Providers referenced by bound and semantic fields. |
observe | ((result: OutputResult) => void | Promise<void>) | undefined | Optional | Read-only. Observer invoked with every composed result. |
Returns: 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; }>; }>.
Return value structure
| Field | Type | Required | Description |
|---|---|---|---|
toModelSchema | (id: string) => { type: string; properties: { [k: string]: Readonly<Record<string, unknown>>; }; required: string[]; additionalProperties: boolean; } | Required | 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[]; } | Required | Validate a generated value without composing it. |
compose | (request: { outputId: string; input: OutputComposeInput; }, context: OperationContext<App>) => OutputResult | Required | 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; }>[] | Required | Read-only. Definitions in registration order; each entry is frozen by the implementation. |
ids | () => readonly string[] | Required | 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; }>> | Required | See the type definition and usage context. |
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; }>]> | Required | See the type definition and usage context. |
has | (id: string) => boolean | Required | See the type definition and usage context. |
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 | Required | 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; }> | Required | 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.
| Type parameter | Constraint | Default type |
|---|---|---|
Context | — | unknown |
| Parameter | Type | Passing | Description |
|---|---|---|---|
options | ReplyFormatOptions<Context> | Required | — |
Fields of options
| Field | Type | Required | Description |
|---|---|---|---|
formats | readonly ReplyFormatDefinition<Context>[] | Required | Read-only. See the type definition and usage context. |
checks | readonly ReplyFormatCheck[] | Required | Read-only. See the type definition and usage context. |
Returns: Readonly<{ resolve(context: Context): CompiledReplyFormat \| undefined; }>.
Return value structure
| Field | Type | Required | Description |
|---|---|---|---|
resolve | (context: Context) => CompiledReplyFormat | undefined | Required | 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.
| Parameter | Type | Passing | Description |
|---|---|---|---|
bubbles | readonly string[] | Required | — |
limit | number | Required | — |
Returns: 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.
| Parameter | Type | Passing | Description |
|---|---|---|---|
master | string | Required | — |
options | TextTemplateOptions | Optional | — |
Fields of options
| Field | Type | Required | Description |
|---|---|---|---|
observe | ((result: string) => void | Promise<void>) | undefined | Optional | Read-only. Observer invoked with every rendered text. |
errors | { readonly missingField?: (key: string) => Error | string; } | undefined | Optional | Read-only. Builds the error for a slot the caller did not supply; receives the slot name. |
Returns: Readonly<{ slots: readonly string[]; render(values: Readonly<Record<string, string>>): string; }>.
Return value structure
| Field | Type | Required | Description |
|---|---|---|---|
slots | readonly string[] | Required | See the type definition and usage context. |
render | (values: Readonly<Record<string, string>>) => string | Required | See the type definition and usage context. |
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.
| Parameter | Type | Passing | Description |
|---|---|---|---|
definition | SemanticSlotDefinition | Required | — |
Fields of definition
| Field | Type | Required | Description |
|---|---|---|---|
namespace | string | Required | Read-only. Slot namespace; must match ^[a-z][a-z0-9-]*$. |
keys | readonly string[] | Required | Read-only. Allowed keys after the namespace; any other key makes the text invalid. |
maxOccurrences | number | Required | Read-only. Maximum total occurrences across the text; must be a non-negative integer. |
Returns: Readonly<{ namespace: string; isValid(text: string): boolean; }>.
Return value structure
| Field | Type | Required | Description |
|---|---|---|---|
namespace | string | Required | See the type definition and usage context. |
isValid | (text: string) => boolean | Required | See the type definition and usage context. |
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.
| Type parameter | Constraint | Default type |
|---|---|---|
Value | — | — |
| Parameter | Type | Passing | Description |
|---|---|---|---|
values | readonly Value[] | Required | — |
options | { previous?: Value; randomIndex(limit: number): number; } | Required | — |
Fields of options
| Field | Type | Required | Description |
|---|---|---|---|
previous | Value | undefined | Optional | See the type definition and usage context. |
randomIndex | (limit: number) => number | Required | See the type definition and usage context. |
Returns: Value \| undefined.
Awaited value: 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.
| Type parameter | Constraint | Default type |
|---|---|---|
Value | — | — |
| Parameter | Type | Passing | Description |
|---|---|---|---|
candidates | readonly { value: Value; priority: number; order: number; }[] | Required | — |
Returns: Value \| undefined.
Awaited value: Awaited<Value> \| undefined.
replyBubbleTexts
Presentation.replyBubbleTexts(reply: ReplyBubbleInput): string[]Collect the non-empty-text parts of a reply.
| Parameter | Type | Passing | Description |
|---|---|---|---|
reply | ReplyBubbleInput | Required | — |
Fields of reply
| Field | Type | Required | Description |
|---|---|---|---|
text | unknown | Optional | Primary reply text. |
bubbles | unknown | Optional | Additional bubbles appended after text, when the value is an array. |
Returns: 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.
| Parameter | Type | Passing | Description |
|---|---|---|---|
request | { text: string; delivery?: ReplyDeliveryOptions; } | Required | — |
context | ExecutionContext | Required | — |
Fields of request
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Required | See the type definition and usage context. |
delivery | ReplyDeliveryOptions | undefined | Optional | See the type definition and usage context. |
Fields of context
| Field | Type | Required | Description |
|---|---|---|---|
logger | SubFlowLog | undefined | Optional | Read-only. Explicit optional recorder for this operation; never inferred from diagnostics. |
signal | AbortSignal | Required | Read-only. Non-optional cancellation signal; never undefined once execution has started. |
diagnostics | DiagnosticContext | undefined | Optional | Read-only. Diagnostic scope of the enclosing call, when recording is enabled. |
Returns: 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.
| Parameter | Type | Passing | Description |
|---|---|---|---|
events | AsyncIterable<AgentOutputEvent> | Required | — |
Returns: AsyncIterable<string>.
textOutputEvents
Presentation.textOutputEvents(chunks: AsyncIterable<string>): AsyncIterable<AgentOutputEvent>Wrap already-chunked text as text events.
| Parameter | Type | Passing | Description |
|---|---|---|---|
chunks | AsyncIterable<string> | Required | — |
Returns: AsyncIterable<AgentOutputEvent>.
Examples
- Presentation: field ownership and final output: The model fills only the fields it is assigned; fixed text, bound values, and semantic keys are owned by the program.
First compose one piece of generated text
Beginner · 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);Let the program own the explanatory prefix in the same compose
Configuration · 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);Add bound fields and semantic keys while keeping the compose entry
Extension · 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);Related policy source
- Field ownership, model Schema, and composition
- Reply format declarations, shared checks, and bubble normalization
Related definitions
AgentOutputEventdefineOutputCatalogdefineReplyFormatsdefineSemanticSlotsdefineTextTemplateExecutionContextmergeReplyBubblesOutputCatalogOptionsOutputDefinitionpublishReplyEventsReplyBubbleInputreplyBubbleTextsReplyFormatOptionsresolvePreferenceselectVariantSemanticSlotDefinitionserializeOutputEventstextOutputEventsTextTemplateOptions