@popcomputer/structured-chat 0.1.0 → 0.2.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -1
- package/README.md +122 -108
- package/dist/adapters/openai-compatible-model.d.ts +4 -4
- package/dist/adapters/openai-compatible-model.js +33 -31
- package/dist/core/answer.d.ts +13 -13
- package/dist/core/answer.js +21 -12
- package/dist/core/chat.d.ts +12 -15
- package/dist/core/chat.js +36 -27
- package/dist/core/collect-stage.d.ts +11 -14
- package/dist/core/collect-stage.js +40 -37
- package/dist/core/command.d.ts +1 -1
- package/dist/core/command.js +1 -1
- package/dist/core/json-value.d.ts +1 -1
- package/dist/core/json-value.js +8 -1
- package/dist/core/model-guard.d.ts +2 -3
- package/dist/core/model-guard.js +4 -4
- package/dist/core/model.d.ts +15 -19
- package/dist/core/model.js +22 -10
- package/dist/core/protocol.d.ts +84 -79
- package/dist/core/protocol.js +18 -12
- package/dist/core/question.js +17 -15
- package/dist/core/repair.js +1 -1
- package/dist/core/session.d.ts +22 -28
- package/dist/core/session.js +16 -7
- package/dist/core/stage-name.d.ts +1 -1
- package/dist/core/stage-name.js +1 -1
- package/dist/core/stage.d.ts +4 -4
- package/dist/core/stage.js +11 -8
- package/dist/core/tool-set.js +6 -6
- package/dist/core/tool.d.ts +36 -39
- package/dist/core/tool.js +58 -31
- package/dist/core/view.d.ts +64 -39
- package/dist/core/view.js +12 -15
- package/dist/integrations/assistant-ui.js +21 -14
- package/dist/testing/in-memory-session-store.js +1 -1
- package/dist/testing/scenario.js +6 -6
- package/examples/answer-modes.ts +60 -49
- package/examples/prompt-injection-policy.ts +20 -20
- package/examples/resource-search.ts +104 -0
- package/package.json +11 -3
- package/examples/agency-search.ts +0 -101
package/dist/core/json-value.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
/** Runtime parser for recursively JSON-serializable values. */
|
|
3
|
-
export const JsonValueSchema = Schema.suspend(() => Schema.Union(
|
|
3
|
+
export const JsonValueSchema = Schema.suspend(() => Schema.Union([
|
|
4
|
+
Schema.String,
|
|
5
|
+
Schema.Finite,
|
|
6
|
+
Schema.Boolean,
|
|
7
|
+
Schema.Null,
|
|
8
|
+
Schema.Array(JsonValueSchema),
|
|
9
|
+
Schema.Record(Schema.String, JsonValueSchema),
|
|
10
|
+
]));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { type StructuredDefinition } from "./definition.js";
|
|
3
3
|
import type { UntrustedMessage } from "./model.js";
|
|
4
|
-
import type { JsonValue } from "./json-value.js";
|
|
5
4
|
/** Stable machine-facing name for one model-boundary policy guard. */
|
|
6
|
-
export declare const ModelGuardNameSchema: Schema.
|
|
5
|
+
export declare const ModelGuardNameSchema: Schema.Trimmed;
|
|
7
6
|
/** Safe context supplied before a structured model request begins. */
|
|
8
7
|
export interface ModelGuardContext {
|
|
9
8
|
readonly messages: ReadonlyArray<UntrustedMessage>;
|
|
@@ -12,7 +11,7 @@ export interface ModelGuardContext {
|
|
|
12
11
|
/** Strictly parsed model proposal supplied before application execution. */
|
|
13
12
|
export interface ModelGuardCall {
|
|
14
13
|
readonly name: string;
|
|
15
|
-
readonly arguments:
|
|
14
|
+
readonly arguments: unknown;
|
|
16
15
|
}
|
|
17
16
|
/** Safe context supplied after parsing and before a tool executes. */
|
|
18
17
|
export interface ModelGuardCallContext extends ModelGuardContext {
|
package/dist/core/model-guard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Effect,
|
|
1
|
+
import { Effect, Function as Fn, Schema } from "effect";
|
|
2
2
|
import { structuredDefinition, } from "./definition.js";
|
|
3
3
|
/** Stable machine-facing name for one model-boundary policy guard. */
|
|
4
|
-
export const ModelGuardNameSchema = Schema.
|
|
4
|
+
export const ModelGuardNameSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100), Schema.isPattern(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/));
|
|
5
5
|
/** Define Effect-native policy checks around a structured model step. */
|
|
6
6
|
export const defineModelGuard = (definition) => {
|
|
7
7
|
Schema.decodeSync(ModelGuardNameSchema)(definition.name);
|
|
@@ -17,7 +17,7 @@ export const defineModelGuard = (definition) => {
|
|
|
17
17
|
const runtimeModelGuard = (guard) => {
|
|
18
18
|
// SAFETY: ModelGuardDefinitionContract carries the package-owned nominal
|
|
19
19
|
// identity and can only be constructed by defineModelGuard.
|
|
20
|
-
return
|
|
20
|
+
return Fn.cast(guard);
|
|
21
21
|
};
|
|
22
22
|
const runGuardPhase = (guards, phase, run) => {
|
|
23
23
|
const execution = Effect.forEach(guards, (guard) => (run(runtimeModelGuard(guard)) ?? Effect.void).pipe(Effect.withSpan("popcomputer.structured_chat.model_guard.check", {
|
|
@@ -25,7 +25,7 @@ const runGuardPhase = (guards, phase, run) => {
|
|
|
25
25
|
})), { concurrency: 1, discard: true });
|
|
26
26
|
// SAFETY: guards run sequentially without recovering failures, so the
|
|
27
27
|
// erased Effect has exactly the conditional error and requirement unions.
|
|
28
|
-
return execution;
|
|
28
|
+
return Fn.cast(execution);
|
|
29
29
|
};
|
|
30
30
|
/** @internal */
|
|
31
31
|
export const runModelGuards = (guards, context) => {
|
package/dist/core/model.d.ts
CHANGED
|
@@ -4,39 +4,35 @@ import { type ModelGuardError, type ModelGuardRequirements, type ModelGuardTuple
|
|
|
4
4
|
import type { ToolSet, ToolSetCall, ToolSetError, ToolSetExecution, ToolSetRequirements, ModelToolTuple, ToolCallPlanner, ToolTuple } from "./tool-set.js";
|
|
5
5
|
import type { JsonValue } from "./json-value.js";
|
|
6
6
|
/** Bounded application-authored instruction supplied to a model adapter. */
|
|
7
|
-
export declare const TrustedInstructionSchema: Schema.brand<Schema.
|
|
7
|
+
export declare const TrustedInstructionSchema: Schema.brand<Schema.Trimmed, "TrustedInstruction">;
|
|
8
8
|
/** Bounded application-authored instruction supplied to a model adapter. */
|
|
9
9
|
export type TrustedInstruction = Schema.Schema.Type<typeof TrustedInstructionSchema>;
|
|
10
10
|
/** Conversation role accepted as untrusted model context. */
|
|
11
|
-
export declare const ConversationRoleSchema: Schema.
|
|
11
|
+
export declare const ConversationRoleSchema: Schema.Literals<readonly ["user", "assistant"]>;
|
|
12
12
|
/** One bounded conversation message treated as untrusted model context. */
|
|
13
13
|
export declare const UntrustedMessageSchema: Schema.Struct<{
|
|
14
|
-
role: Schema.
|
|
15
|
-
content: Schema.
|
|
14
|
+
readonly role: Schema.Literals<readonly ["user", "assistant"]>;
|
|
15
|
+
readonly content: Schema.Trimmed;
|
|
16
16
|
}>;
|
|
17
17
|
/** One bounded conversation message treated as untrusted model context. */
|
|
18
18
|
export type UntrustedMessage = Schema.Schema.Type<typeof UntrustedMessageSchema>;
|
|
19
19
|
/** @internal Exact content-character count for bounded message arrays. */
|
|
20
20
|
export declare const countUntrustedMessageCharacters: (messages: ReadonlyArray<UntrustedMessage>) => number;
|
|
21
21
|
/** Safe reason that a configured chat model could not complete a step. */
|
|
22
|
-
export declare const ChatModelUnavailableReasonSchema: Schema.
|
|
23
|
-
declare const ChatModelUnavailable_base: Schema.
|
|
24
|
-
readonly
|
|
25
|
-
}
|
|
26
|
-
reason: Schema.Literal<["request_failed", "timed_out", "response_blocked", "invalid_response"]>;
|
|
27
|
-
}>;
|
|
22
|
+
export declare const ChatModelUnavailableReasonSchema: Schema.Literals<readonly ["request_failed", "timed_out", "response_blocked", "invalid_response"]>;
|
|
23
|
+
declare const ChatModelUnavailable_base: Schema.Class<ChatModelUnavailable, Schema.TaggedStruct<"ChatModelUnavailable", {
|
|
24
|
+
readonly reason: Schema.Literals<readonly ["request_failed", "timed_out", "response_blocked", "invalid_response"]>;
|
|
25
|
+
}>, import("effect/Cause").YieldableError>;
|
|
28
26
|
/** A configured chat model could not complete a structured step. */
|
|
29
27
|
export declare class ChatModelUnavailable extends ChatModelUnavailable_base {
|
|
30
28
|
}
|
|
31
29
|
/** Safe reason that a tool schema cannot use strict provider decoding. */
|
|
32
|
-
export declare const UnsupportedModelToolSchemaReasonSchema: Schema.
|
|
33
|
-
declare const UnsupportedModelToolSchema_base: Schema.
|
|
34
|
-
readonly
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
reason: Schema.Literal<["root_not_object", "additional_properties_allowed", "optional_property"]>;
|
|
39
|
-
}>;
|
|
30
|
+
export declare const UnsupportedModelToolSchemaReasonSchema: Schema.Literals<readonly ["root_not_object", "additional_properties_allowed", "optional_property"]>;
|
|
31
|
+
declare const UnsupportedModelToolSchema_base: Schema.Class<UnsupportedModelToolSchema, Schema.TaggedStruct<"UnsupportedModelToolSchema", {
|
|
32
|
+
readonly tool: Schema.Trimmed;
|
|
33
|
+
readonly path: Schema.Trimmed;
|
|
34
|
+
readonly reason: Schema.Literals<readonly ["root_not_object", "additional_properties_allowed", "optional_property"]>;
|
|
35
|
+
}>, import("effect/Cause").YieldableError>;
|
|
40
36
|
/** A model tool schema is incompatible with strict provider decoding. */
|
|
41
37
|
export declare class UnsupportedModelToolSchema extends UnsupportedModelToolSchema_base {
|
|
42
38
|
}
|
|
@@ -54,7 +50,7 @@ export interface StructuredChatModelService {
|
|
|
54
50
|
/** Return one untrusted provider tool call for runtime validation. */
|
|
55
51
|
readonly requestTool: (request: ToolModelRequest) => Effect.Effect<JsonValue, ChatModelUnavailable | UnsupportedModelToolSchema>;
|
|
56
52
|
}
|
|
57
|
-
declare const StructuredChatModel_base: Context.
|
|
53
|
+
declare const StructuredChatModel_base: Context.ServiceClass<StructuredChatModel, "@popcomputer/structured-chat/StructuredChatModel", StructuredChatModelService>;
|
|
58
54
|
/** Effect service for the configured structured chat model adapter. */
|
|
59
55
|
export declare class StructuredChatModel extends StructuredChatModel_base {
|
|
60
56
|
}
|
package/dist/core/model.js
CHANGED
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
import { Context, Effect, Schema } from "effect";
|
|
2
2
|
import { runModelCallGuards, runModelGuards, } from "./model-guard.js";
|
|
3
3
|
/** Bounded application-authored instruction supplied to a model adapter. */
|
|
4
|
-
export const TrustedInstructionSchema = Schema.
|
|
4
|
+
export const TrustedInstructionSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(20_000)).pipe(Schema.brand("TrustedInstruction"));
|
|
5
5
|
/** Conversation role accepted as untrusted model context. */
|
|
6
|
-
export const ConversationRoleSchema = Schema.
|
|
6
|
+
export const ConversationRoleSchema = Schema.Literals([
|
|
7
|
+
"user",
|
|
8
|
+
"assistant",
|
|
9
|
+
]);
|
|
7
10
|
/** One bounded conversation message treated as untrusted model context. */
|
|
8
11
|
export const UntrustedMessageSchema = Schema.Struct({
|
|
9
12
|
role: ConversationRoleSchema,
|
|
10
|
-
content: Schema.
|
|
13
|
+
content: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(50_000)),
|
|
11
14
|
});
|
|
12
15
|
/** @internal Exact content-character count for bounded message arrays. */
|
|
13
16
|
export const countUntrustedMessageCharacters = (messages) => messages.reduce((total, message) => total + message.content.length, 0);
|
|
14
17
|
/** Safe reason that a configured chat model could not complete a step. */
|
|
15
|
-
export const ChatModelUnavailableReasonSchema = Schema.
|
|
18
|
+
export const ChatModelUnavailableReasonSchema = Schema.Literals([
|
|
19
|
+
"request_failed",
|
|
20
|
+
"timed_out",
|
|
21
|
+
"response_blocked",
|
|
22
|
+
"invalid_response",
|
|
23
|
+
]);
|
|
16
24
|
/** A configured chat model could not complete a structured step. */
|
|
17
25
|
export class ChatModelUnavailable extends Schema.TaggedError()("ChatModelUnavailable", { reason: ChatModelUnavailableReasonSchema }) {
|
|
18
26
|
}
|
|
19
27
|
/** Safe reason that a tool schema cannot use strict provider decoding. */
|
|
20
|
-
export const UnsupportedModelToolSchemaReasonSchema = Schema.
|
|
28
|
+
export const UnsupportedModelToolSchemaReasonSchema = Schema.Literals([
|
|
29
|
+
"root_not_object",
|
|
30
|
+
"additional_properties_allowed",
|
|
31
|
+
"optional_property",
|
|
32
|
+
]);
|
|
21
33
|
/** A model tool schema is incompatible with strict provider decoding. */
|
|
22
34
|
export class UnsupportedModelToolSchema extends Schema.TaggedError()("UnsupportedModelToolSchema", {
|
|
23
|
-
tool: Schema.
|
|
24
|
-
path: Schema.
|
|
35
|
+
tool: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100)),
|
|
36
|
+
path: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(2_000)),
|
|
25
37
|
reason: UnsupportedModelToolSchemaReasonSchema,
|
|
26
38
|
}) {
|
|
27
39
|
}
|
|
28
40
|
/** Effect service for the configured structured chat model adapter. */
|
|
29
|
-
export class StructuredChatModel extends Context.
|
|
41
|
+
export class StructuredChatModel extends Context.Service()("@popcomputer/structured-chat/StructuredChatModel") {
|
|
30
42
|
}
|
|
31
43
|
const invalidOutputRepairInstruction = Schema.decodeSync(TrustedInstructionSchema)("Your previous response did not satisfy the required tool-call contract. Call exactly one listed tool and return only arguments allowed by its JSON Schema.");
|
|
32
44
|
const isRepairableModelOutput = (error) => error._tag === "InvalidToolCall" ||
|
|
@@ -36,7 +48,7 @@ const isRepairableModelOutput = (error) => error._tag === "InvalidToolCall" ||
|
|
|
36
48
|
export const planToolCall = (input) => runModelGuards(input.guards ?? [], {
|
|
37
49
|
messages: input.messages,
|
|
38
50
|
toolNames: input.tools.models.map(({ name }) => name),
|
|
39
|
-
}).pipe(Effect.
|
|
51
|
+
}).pipe(Effect.andThen(StructuredChatModel), Effect.flatMap((model) => {
|
|
40
52
|
const requestParsedCall = (instructions, attempt) => model
|
|
41
53
|
.requestTool({
|
|
42
54
|
instructions,
|
|
@@ -68,7 +80,7 @@ export const planToolCall = (input) => runModelGuards(input.guards ?? [], {
|
|
|
68
80
|
errorTag: error._tag,
|
|
69
81
|
errorReason: error.reason,
|
|
70
82
|
};
|
|
71
|
-
return Effect.logWarning("Retrying structured model output").pipe(Effect.annotateLogs(annotations), Effect.
|
|
83
|
+
return Effect.logWarning("Retrying structured model output").pipe(Effect.annotateLogs(annotations), Effect.andThen(requestParsedCall([
|
|
72
84
|
...input.instructions,
|
|
73
85
|
invalidOutputRepairInstruction,
|
|
74
86
|
], 2)));
|
package/dist/core/protocol.d.ts
CHANGED
|
@@ -2,74 +2,74 @@ import { Effect, Schema } from "effect";
|
|
|
2
2
|
import { ChatSessionIdSchema, ChatSessionRevisionSchema } from "./session.js";
|
|
3
3
|
/** Bounded plain text emitted by a structured chat presenter. */
|
|
4
4
|
export declare const AssistantTextPartSchema: Schema.Struct<{
|
|
5
|
-
type: Schema.Literal<
|
|
6
|
-
text: Schema.
|
|
5
|
+
readonly type: Schema.Literal<"text">;
|
|
6
|
+
readonly text: Schema.Trimmed;
|
|
7
7
|
}>;
|
|
8
8
|
/** Provider-neutral named data emitted by a structured chat presenter. */
|
|
9
9
|
export declare const AssistantDataPartSchema: Schema.Struct<{
|
|
10
|
-
type: Schema.Literal<
|
|
11
|
-
name: Schema.
|
|
12
|
-
data:
|
|
10
|
+
readonly type: Schema.Literal<"data">;
|
|
11
|
+
readonly name: Schema.Trimmed;
|
|
12
|
+
readonly data: Schema.Unknown;
|
|
13
13
|
}>;
|
|
14
14
|
/** Message parts transported from a structured chat action to a browser. */
|
|
15
|
-
export declare const AssistantMessagePartSchema: Schema.Union<[Schema.Struct<{
|
|
16
|
-
type: Schema.Literal<
|
|
17
|
-
text: Schema.
|
|
15
|
+
export declare const AssistantMessagePartSchema: Schema.Union<readonly [Schema.Struct<{
|
|
16
|
+
readonly type: Schema.Literal<"text">;
|
|
17
|
+
readonly text: Schema.Trimmed;
|
|
18
18
|
}>, Schema.Struct<{
|
|
19
|
-
type: Schema.Literal<
|
|
20
|
-
name: Schema.
|
|
21
|
-
data:
|
|
19
|
+
readonly type: Schema.Literal<"data">;
|
|
20
|
+
readonly name: Schema.Trimmed;
|
|
21
|
+
readonly data: Schema.Unknown;
|
|
22
22
|
}>]>;
|
|
23
23
|
/** Message parts transported from a structured chat action to a browser. */
|
|
24
24
|
export type AssistantMessagePart = Schema.Schema.Type<typeof AssistantMessagePartSchema>;
|
|
25
25
|
/** Strict assistant message returned by one structured chat action. */
|
|
26
26
|
export declare const StructuredChatAssistantMessageSchema: Schema.Struct<{
|
|
27
|
-
role: Schema.Literal<
|
|
28
|
-
content: Schema.
|
|
29
|
-
type: Schema.Literal<
|
|
30
|
-
text: Schema.
|
|
27
|
+
readonly role: Schema.Literal<"assistant">;
|
|
28
|
+
readonly content: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
29
|
+
readonly type: Schema.Literal<"text">;
|
|
30
|
+
readonly text: Schema.Trimmed;
|
|
31
31
|
}>, Schema.Struct<{
|
|
32
|
-
type: Schema.Literal<
|
|
33
|
-
name: Schema.
|
|
34
|
-
data:
|
|
35
|
-
}>]
|
|
32
|
+
readonly type: Schema.Literal<"data">;
|
|
33
|
+
readonly name: Schema.Trimmed;
|
|
34
|
+
readonly data: Schema.Unknown;
|
|
35
|
+
}>]>>;
|
|
36
36
|
}>;
|
|
37
37
|
/** Strict assistant message returned by one structured chat action. */
|
|
38
38
|
export type StructuredChatAssistantMessage = Schema.Schema.Type<typeof StructuredChatAssistantMessageSchema>;
|
|
39
39
|
/** Opaque browser-held reference to one server-owned chat session. */
|
|
40
40
|
export declare const StructuredChatSessionReferenceSchema: Schema.Struct<{
|
|
41
|
-
id: Schema.
|
|
42
|
-
revision: Schema.
|
|
41
|
+
readonly id: Schema.Trimmed;
|
|
42
|
+
readonly revision: Schema.Trimmed;
|
|
43
43
|
}>;
|
|
44
44
|
/** Opaque browser-held reference to one server-owned chat session. */
|
|
45
45
|
export type StructuredChatSessionReference = Schema.Schema.Type<typeof StructuredChatSessionReferenceSchema>;
|
|
46
46
|
/** Browser request carrying no server-owned chat state. */
|
|
47
47
|
export declare const StructuredChatTurnRequestSchema: Schema.Struct<{
|
|
48
|
-
session: Schema.optional<Schema.Struct<{
|
|
49
|
-
id: Schema.
|
|
50
|
-
revision: Schema.
|
|
48
|
+
readonly session: Schema.optional<Schema.Struct<{
|
|
49
|
+
readonly id: Schema.Trimmed;
|
|
50
|
+
readonly revision: Schema.Trimmed;
|
|
51
51
|
}>>;
|
|
52
|
-
message: Schema.
|
|
52
|
+
readonly message: Schema.Trimmed;
|
|
53
53
|
}>;
|
|
54
54
|
/** Browser request carrying no server-owned chat state. */
|
|
55
55
|
export type StructuredChatTurnRequest = Schema.Schema.Type<typeof StructuredChatTurnRequestSchema>;
|
|
56
56
|
/** Versioned browser response for one persisted structured chat turn. */
|
|
57
57
|
export declare const StructuredChatTurnResponseSchema: Schema.Struct<{
|
|
58
|
-
schemaVersion: Schema.Literal<
|
|
59
|
-
session: Schema.optional<Schema.Struct<{
|
|
60
|
-
id: Schema.
|
|
61
|
-
revision: Schema.
|
|
58
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
59
|
+
readonly session: Schema.optional<Schema.Struct<{
|
|
60
|
+
readonly id: Schema.Trimmed;
|
|
61
|
+
readonly revision: Schema.Trimmed;
|
|
62
62
|
}>>;
|
|
63
|
-
message: Schema.Struct<{
|
|
64
|
-
role: Schema.Literal<
|
|
65
|
-
content: Schema.
|
|
66
|
-
type: Schema.Literal<
|
|
67
|
-
text: Schema.
|
|
63
|
+
readonly message: Schema.Struct<{
|
|
64
|
+
readonly role: Schema.Literal<"assistant">;
|
|
65
|
+
readonly content: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
66
|
+
readonly type: Schema.Literal<"text">;
|
|
67
|
+
readonly text: Schema.Trimmed;
|
|
68
68
|
}>, Schema.Struct<{
|
|
69
|
-
type: Schema.Literal<
|
|
70
|
-
name: Schema.
|
|
71
|
-
data:
|
|
72
|
-
}>]
|
|
69
|
+
readonly type: Schema.Literal<"data">;
|
|
70
|
+
readonly name: Schema.Trimmed;
|
|
71
|
+
readonly data: Schema.Unknown;
|
|
72
|
+
}>]>>;
|
|
73
73
|
}>;
|
|
74
74
|
}>;
|
|
75
75
|
/** Versioned browser response for one persisted structured chat turn. */
|
|
@@ -79,102 +79,107 @@ export declare const CollectQuestionView: {
|
|
|
79
79
|
name: "collect_question";
|
|
80
80
|
version: 1;
|
|
81
81
|
inputSchema: Schema.Struct<{
|
|
82
|
-
stage: Schema.
|
|
83
|
-
field: Schema.
|
|
84
|
-
text: Schema.
|
|
85
|
-
options: Schema
|
|
86
|
-
label: Schema.
|
|
87
|
-
}
|
|
82
|
+
readonly stage: Schema.Trimmed;
|
|
83
|
+
readonly field: Schema.Trimmed;
|
|
84
|
+
readonly text: Schema.Trimmed;
|
|
85
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
86
|
+
readonly label: Schema.Trimmed;
|
|
87
|
+
}>>;
|
|
88
88
|
}>;
|
|
89
89
|
dataSchema: Schema.Struct<{
|
|
90
|
-
readonly schemaVersion: Schema.Literal<
|
|
90
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
91
91
|
} & {
|
|
92
|
-
stage: Schema.
|
|
93
|
-
field: Schema.
|
|
94
|
-
text: Schema.
|
|
95
|
-
options: Schema
|
|
96
|
-
label: Schema.
|
|
97
|
-
}
|
|
92
|
+
readonly stage: Schema.Trimmed;
|
|
93
|
+
readonly field: Schema.Trimmed;
|
|
94
|
+
readonly text: Schema.Trimmed;
|
|
95
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
96
|
+
readonly label: Schema.Trimmed;
|
|
97
|
+
}>>;
|
|
98
98
|
}>;
|
|
99
99
|
partSchema: Schema.Struct<{
|
|
100
|
-
readonly type: Schema.Literal<
|
|
101
|
-
readonly name: Schema.Literal<
|
|
100
|
+
readonly type: Schema.Literal<"data">;
|
|
101
|
+
readonly name: Schema.Literal<"collect_question">;
|
|
102
102
|
readonly data: Schema.Struct<{
|
|
103
|
-
readonly schemaVersion: Schema.Literal<
|
|
103
|
+
readonly schemaVersion: Schema.Literal<1>;
|
|
104
104
|
} & {
|
|
105
|
-
stage: Schema.
|
|
106
|
-
field: Schema.
|
|
107
|
-
text: Schema.
|
|
108
|
-
options: Schema
|
|
109
|
-
label: Schema.
|
|
110
|
-
}
|
|
105
|
+
readonly stage: Schema.Trimmed;
|
|
106
|
+
readonly field: Schema.Trimmed;
|
|
107
|
+
readonly text: Schema.Trimmed;
|
|
108
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
109
|
+
readonly label: Schema.Trimmed;
|
|
110
|
+
}>>;
|
|
111
111
|
}>;
|
|
112
112
|
}>;
|
|
113
113
|
make: (input: {
|
|
114
|
-
readonly text: string;
|
|
115
114
|
readonly stage: string;
|
|
116
115
|
readonly field: string;
|
|
116
|
+
readonly text: string;
|
|
117
117
|
readonly options: readonly {
|
|
118
118
|
readonly label: string;
|
|
119
119
|
}[];
|
|
120
120
|
}) => {
|
|
121
|
-
readonly name: "collect_question";
|
|
122
121
|
readonly type: "data";
|
|
122
|
+
readonly name: "collect_question";
|
|
123
123
|
readonly data: {
|
|
124
124
|
readonly schemaVersion: 1;
|
|
125
|
-
readonly text: string;
|
|
126
125
|
readonly stage: string;
|
|
127
126
|
readonly field: string;
|
|
127
|
+
readonly text: string;
|
|
128
128
|
readonly options: readonly {
|
|
129
129
|
readonly label: string;
|
|
130
130
|
}[];
|
|
131
131
|
};
|
|
132
132
|
};
|
|
133
|
-
parseData: (input:
|
|
134
|
-
readonly
|
|
133
|
+
parseData: (input: {
|
|
134
|
+
readonly stage: string;
|
|
135
|
+
readonly field: string;
|
|
136
|
+
readonly text: string;
|
|
137
|
+
readonly options: readonly {
|
|
138
|
+
readonly label: string;
|
|
139
|
+
}[];
|
|
140
|
+
}) => Effect.Effect<{
|
|
135
141
|
readonly type: "data";
|
|
142
|
+
readonly name: "collect_question";
|
|
136
143
|
readonly data: {
|
|
137
144
|
readonly schemaVersion: 1;
|
|
138
|
-
readonly text: string;
|
|
139
145
|
readonly stage: string;
|
|
140
146
|
readonly field: string;
|
|
147
|
+
readonly text: string;
|
|
141
148
|
readonly options: readonly {
|
|
142
149
|
readonly label: string;
|
|
143
150
|
}[];
|
|
144
151
|
};
|
|
145
|
-
},
|
|
152
|
+
}, Schema.SchemaError, never>;
|
|
146
153
|
decode: (input: import("./json-value.js").JsonValue) => Effect.Effect<{
|
|
147
|
-
readonly name: "collect_question";
|
|
148
154
|
readonly type: "data";
|
|
155
|
+
readonly name: "collect_question";
|
|
149
156
|
readonly data: {
|
|
150
157
|
readonly schemaVersion: 1;
|
|
151
|
-
readonly text: string;
|
|
152
158
|
readonly stage: string;
|
|
153
159
|
readonly field: string;
|
|
160
|
+
readonly text: string;
|
|
154
161
|
readonly options: readonly {
|
|
155
162
|
readonly label: string;
|
|
156
163
|
}[];
|
|
157
164
|
};
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
readonly name: "collect_question";
|
|
165
|
+
}, Schema.SchemaError, never>;
|
|
166
|
+
decodeResult: (input: import("./json-value.js").JsonValue) => import("effect/Result").Result<{
|
|
161
167
|
readonly type: "data";
|
|
168
|
+
readonly name: "collect_question";
|
|
162
169
|
readonly data: {
|
|
163
170
|
readonly schemaVersion: 1;
|
|
164
|
-
readonly text: string;
|
|
165
171
|
readonly stage: string;
|
|
166
172
|
readonly field: string;
|
|
173
|
+
readonly text: string;
|
|
167
174
|
readonly options: readonly {
|
|
168
175
|
readonly label: string;
|
|
169
176
|
}[];
|
|
170
177
|
};
|
|
171
|
-
},
|
|
178
|
+
}, Schema.SchemaError>;
|
|
172
179
|
};
|
|
173
|
-
declare const InvalidChatPresentation_base: Schema.
|
|
174
|
-
readonly
|
|
175
|
-
}
|
|
176
|
-
reason: Schema.Literal<["invalid_message"]>;
|
|
177
|
-
}>;
|
|
180
|
+
declare const InvalidChatPresentation_base: Schema.Class<InvalidChatPresentation, Schema.TaggedStruct<"InvalidChatPresentation", {
|
|
181
|
+
readonly reason: Schema.Literal<"invalid_message">;
|
|
182
|
+
}>, import("effect/Cause").YieldableError>;
|
|
178
183
|
/** Safe reason that application-owned message presentation was rejected. */
|
|
179
184
|
export declare class InvalidChatPresentation extends InvalidChatPresentation_base {
|
|
180
185
|
}
|
package/dist/core/protocol.js
CHANGED
|
@@ -4,20 +4,23 @@ import { defineView } from "./view.js";
|
|
|
4
4
|
/** Bounded plain text emitted by a structured chat presenter. */
|
|
5
5
|
export const AssistantTextPartSchema = Schema.Struct({
|
|
6
6
|
type: Schema.Literal("text"),
|
|
7
|
-
text: Schema.
|
|
7
|
+
text: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(20_000)),
|
|
8
8
|
});
|
|
9
9
|
/** Provider-neutral named data emitted by a structured chat presenter. */
|
|
10
10
|
export const AssistantDataPartSchema = Schema.Struct({
|
|
11
11
|
type: Schema.Literal("data"),
|
|
12
|
-
name: Schema.
|
|
12
|
+
name: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100), Schema.isPattern(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/)),
|
|
13
13
|
data: Schema.Unknown,
|
|
14
14
|
});
|
|
15
15
|
/** Message parts transported from a structured chat action to a browser. */
|
|
16
|
-
export const AssistantMessagePartSchema = Schema.Union(
|
|
16
|
+
export const AssistantMessagePartSchema = Schema.Union([
|
|
17
|
+
AssistantTextPartSchema,
|
|
18
|
+
AssistantDataPartSchema,
|
|
19
|
+
]);
|
|
17
20
|
/** Strict assistant message returned by one structured chat action. */
|
|
18
21
|
export const StructuredChatAssistantMessageSchema = Schema.Struct({
|
|
19
22
|
role: Schema.Literal("assistant"),
|
|
20
|
-
content: Schema.NonEmptyArray(AssistantMessagePartSchema).
|
|
23
|
+
content: Schema.NonEmptyArray(AssistantMessagePartSchema).check(Schema.isMaxLength(20)),
|
|
21
24
|
});
|
|
22
25
|
/** Opaque browser-held reference to one server-owned chat session. */
|
|
23
26
|
export const StructuredChatSessionReferenceSchema = Schema.Struct({
|
|
@@ -27,7 +30,7 @@ export const StructuredChatSessionReferenceSchema = Schema.Struct({
|
|
|
27
30
|
/** Browser request carrying no server-owned chat state. */
|
|
28
31
|
export const StructuredChatTurnRequestSchema = Schema.Struct({
|
|
29
32
|
session: Schema.optional(StructuredChatSessionReferenceSchema),
|
|
30
|
-
message: Schema.
|
|
33
|
+
message: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(50_000)),
|
|
31
34
|
});
|
|
32
35
|
/** Versioned browser response for one persisted structured chat turn. */
|
|
33
36
|
export const StructuredChatTurnResponseSchema = Schema.Struct({
|
|
@@ -40,25 +43,28 @@ export const CollectQuestionView = defineView({
|
|
|
40
43
|
name: "collect_question",
|
|
41
44
|
version: 1,
|
|
42
45
|
schema: Schema.Struct({
|
|
43
|
-
stage: Schema.
|
|
44
|
-
field: Schema.
|
|
45
|
-
text: Schema.
|
|
46
|
+
stage: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100)),
|
|
47
|
+
field: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100)),
|
|
48
|
+
text: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(500)),
|
|
46
49
|
options: Schema.Array(Schema.Struct({
|
|
47
|
-
label: Schema.
|
|
48
|
-
})).
|
|
50
|
+
label: Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100)),
|
|
51
|
+
})).check(Schema.isMaxLength(20)),
|
|
49
52
|
}),
|
|
50
53
|
});
|
|
51
54
|
/** Safe reason that application-owned message presentation was rejected. */
|
|
52
55
|
export class InvalidChatPresentation extends Schema.TaggedError()("InvalidChatPresentation", { reason: Schema.Literal("invalid_message") }) {
|
|
53
56
|
}
|
|
54
57
|
/** Construct and validate one plain assistant text part. */
|
|
55
|
-
const makeText = (text) => Schema.
|
|
58
|
+
const makeText = (text) => Schema.decodeSync(Schema.toType(AssistantTextPartSchema))({
|
|
59
|
+
type: "text",
|
|
60
|
+
text,
|
|
61
|
+
});
|
|
56
62
|
/** Constructors for deterministic assistant message parts. */
|
|
57
63
|
export const Text = {
|
|
58
64
|
make: makeText,
|
|
59
65
|
};
|
|
60
66
|
const invalidPresentation = () => new InvalidChatPresentation({ reason: "invalid_message" });
|
|
61
|
-
const parseResponse = (input) => Schema.
|
|
67
|
+
const parseResponse = (input) => Schema.decodeUnknownEffect(StructuredChatTurnResponseSchema)(input, {
|
|
62
68
|
onExcessProperty: "error",
|
|
63
69
|
}).pipe(Effect.mapError(invalidPresentation));
|
|
64
70
|
const buildPresentation = (evaluate) => Effect.try({
|
package/dist/core/question.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Schema
|
|
2
|
-
const QuestionTextSchema = Schema.
|
|
3
|
-
const QuestionGoalSchema = Schema.
|
|
4
|
-
const ChoiceLabelSchema = Schema.
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
const QuestionTextSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(500));
|
|
3
|
+
const QuestionGoalSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(1_000));
|
|
4
|
+
const ChoiceLabelSchema = Schema.Trimmed.check(Schema.isNonEmpty(), Schema.isMaxLength(100));
|
|
5
|
+
const ChoiceCountSchema = Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 1, maximum: 20 }));
|
|
5
6
|
const fixed = (text) => ({
|
|
6
7
|
_tag: "FixedQuestion",
|
|
7
8
|
text: Schema.decodeSync(QuestionTextSchema)(text),
|
|
@@ -12,8 +13,8 @@ const adaptive = (goal, options) => ({
|
|
|
12
13
|
fallback: Schema.decodeSync(QuestionTextSchema)(options.fallback),
|
|
13
14
|
});
|
|
14
15
|
const adaptiveChoice = (prompt, options) => {
|
|
15
|
-
const minimumOptions = Schema.decodeSync(
|
|
16
|
-
const maximumOptions = Schema.decodeSync(
|
|
16
|
+
const minimumOptions = Schema.decodeSync(ChoiceCountSchema)(options.minimumOptions);
|
|
17
|
+
const maximumOptions = Schema.decodeSync(ChoiceCountSchema)(options.maximumOptions);
|
|
17
18
|
if (minimumOptions > maximumOptions) {
|
|
18
19
|
throw new Error("Adaptive choice minimumOptions cannot exceed maximumOptions");
|
|
19
20
|
}
|
|
@@ -36,21 +37,22 @@ const adaptiveChoice = (prompt, options) => {
|
|
|
36
37
|
};
|
|
37
38
|
};
|
|
38
39
|
const choice = (text, options) => {
|
|
39
|
-
const
|
|
40
|
-
|
|
40
|
+
const firstOption = {
|
|
41
|
+
...options[0],
|
|
42
|
+
label: Schema.decodeSync(ChoiceLabelSchema)(options[0].label),
|
|
43
|
+
};
|
|
44
|
+
const remainingOptions = options.slice(1).map((option) => ({
|
|
45
|
+
...option,
|
|
46
|
+
label: Schema.decodeSync(ChoiceLabelSchema)(option.label),
|
|
47
|
+
}));
|
|
48
|
+
const normalized = [firstOption, ...remainingOptions].map(({ label }) => label.toLocaleLowerCase("en"));
|
|
41
49
|
if (new Set(normalized).size !== normalized.length) {
|
|
42
50
|
throw new Error("Choice question labels must be unique");
|
|
43
51
|
}
|
|
44
|
-
const parsedOptions = options.map((option, index) => ({
|
|
45
|
-
...option,
|
|
46
|
-
label: labels[index] ?? option.label,
|
|
47
|
-
}));
|
|
48
|
-
// SAFETY: map preserves the non-empty tuple length and each option's value;
|
|
49
|
-
// every replacement label was parsed at the same array index.
|
|
50
52
|
return {
|
|
51
53
|
_tag: "ChoiceQuestion",
|
|
52
54
|
text: Schema.decodeSync(QuestionTextSchema)(text),
|
|
53
|
-
options:
|
|
55
|
+
options: [firstOption, ...remainingOptions],
|
|
54
56
|
};
|
|
55
57
|
};
|
|
56
58
|
/** Constructors for static, adaptive, and typed choice questions. */
|
package/dist/core/repair.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { structuredDefinition, } from "./definition.js";
|
|
3
|
-
const maximumCorrectionsSchema = Schema.Number.
|
|
3
|
+
const maximumCorrectionsSchema = Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 1, maximum: 20 }));
|
|
4
4
|
const standard = (options = {}) => structuredDefinition("repair")({
|
|
5
5
|
_tag: "StandardRepair",
|
|
6
6
|
maximumCorrections: Schema.decodeSync(maximumCorrectionsSchema)(options.maximumCorrections ?? 5),
|