effect-assistant 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +22 -2
- package/dist/AppEvent.d.ts +111 -0
- package/dist/AppEvent.d.ts.map +1 -0
- package/dist/AppEvent.js +71 -0
- package/dist/AppEvent.js.map +1 -0
- package/dist/Assistant.d.ts +403 -0
- package/dist/Assistant.d.ts.map +1 -0
- package/dist/Assistant.js +126 -0
- package/dist/Assistant.js.map +1 -0
- package/dist/AssistantActivity.d.ts +85 -0
- package/dist/AssistantActivity.d.ts.map +1 -0
- package/dist/AssistantActivity.js +105 -0
- package/dist/AssistantActivity.js.map +1 -0
- package/dist/AssistantEngine.d.ts +116 -0
- package/dist/AssistantEngine.d.ts.map +1 -0
- package/dist/AssistantEngine.js +255 -0
- package/dist/AssistantEngine.js.map +1 -0
- package/dist/AssistantEvent.d.ts +551 -0
- package/dist/AssistantEvent.d.ts.map +1 -0
- package/dist/AssistantEvent.js +473 -0
- package/dist/AssistantEvent.js.map +1 -0
- package/dist/AssistantLoop.d.ts +173 -0
- package/dist/AssistantLoop.d.ts.map +1 -0
- package/dist/AssistantLoop.js +111 -0
- package/dist/AssistantLoop.js.map +1 -0
- package/dist/AssistantToolCall.d.ts +32 -0
- package/dist/AssistantToolCall.d.ts.map +1 -0
- package/dist/AssistantToolCall.js +37 -0
- package/dist/AssistantToolCall.js.map +1 -0
- package/dist/Block.d.ts +69 -0
- package/dist/Block.d.ts.map +1 -0
- package/dist/Block.js +39 -0
- package/dist/Block.js.map +1 -0
- package/dist/BlockKit.d.ts +180 -0
- package/dist/BlockKit.d.ts.map +1 -0
- package/dist/BlockKit.js +148 -0
- package/dist/BlockKit.js.map +1 -0
- package/dist/ChatId.d.ts +31 -0
- package/dist/ChatId.d.ts.map +1 -0
- package/dist/ChatId.js +36 -0
- package/dist/ChatId.js.map +1 -0
- package/dist/ChatLog.d.ts +109 -0
- package/dist/ChatLog.d.ts.map +1 -0
- package/dist/ChatLog.js +177 -0
- package/dist/ChatLog.js.map +1 -0
- package/dist/ChatView.d.ts +408 -0
- package/dist/ChatView.d.ts.map +1 -0
- package/dist/ChatView.js +443 -0
- package/dist/ChatView.js.map +1 -0
- package/dist/Chats.d.ts +110 -0
- package/dist/Chats.d.ts.map +1 -0
- package/dist/Chats.js +98 -0
- package/dist/Chats.js.map +1 -0
- package/dist/Compaction.d.ts +105 -0
- package/dist/Compaction.d.ts.map +1 -0
- package/dist/Compaction.js +126 -0
- package/dist/Compaction.js.map +1 -0
- package/dist/CurrentChat.d.ts +40 -0
- package/dist/CurrentChat.d.ts.map +1 -0
- package/dist/CurrentChat.js +33 -0
- package/dist/CurrentChat.js.map +1 -0
- package/dist/InputId.d.ts +38 -0
- package/dist/InputId.d.ts.map +1 -0
- package/dist/InputId.js +43 -0
- package/dist/InputId.js.map +1 -0
- package/dist/Instructions.d.ts +56 -0
- package/dist/Instructions.d.ts.map +1 -0
- package/dist/Instructions.js +50 -0
- package/dist/Instructions.js.map +1 -0
- package/dist/Memory.d.ts +190 -0
- package/dist/Memory.d.ts.map +1 -0
- package/dist/Memory.js +216 -0
- package/dist/Memory.js.map +1 -0
- package/dist/RunId.d.ts +41 -0
- package/dist/RunId.d.ts.map +1 -0
- package/dist/RunId.js +46 -0
- package/dist/RunId.js.map +1 -0
- package/dist/Skill.d.ts +72 -0
- package/dist/Skill.d.ts.map +1 -0
- package/dist/Skill.js +58 -0
- package/dist/Skill.js.map +1 -0
- package/dist/Specialist.d.ts +267 -0
- package/dist/Specialist.d.ts.map +1 -0
- package/dist/Specialist.js +119 -0
- package/dist/Specialist.js.map +1 -0
- package/dist/ToolDiscovery.d.ts +107 -0
- package/dist/ToolDiscovery.d.ts.map +1 -0
- package/dist/ToolDiscovery.js +140 -0
- package/dist/ToolDiscovery.js.map +1 -0
- package/dist/ToolFailure.d.ts +66 -0
- package/dist/ToolFailure.d.ts.map +1 -0
- package/dist/ToolFailure.js +79 -0
- package/dist/ToolFailure.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/internal/chat.js +177 -0
- package/dist/internal/chat.js.map +1 -0
- package/dist/internal/events.d.ts +46 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +65 -0
- package/dist/internal/events.js.map +1 -0
- package/dist/internal/history.js +147 -0
- package/dist/internal/history.js.map +1 -0
- package/dist/internal/instructions.js +11 -0
- package/dist/internal/instructions.js.map +1 -0
- package/dist/internal/loop.d.ts +73 -0
- package/dist/internal/loop.d.ts.map +1 -0
- package/dist/internal/loop.js +131 -0
- package/dist/internal/loop.js.map +1 -0
- package/dist/internal/schema.js +14 -0
- package/dist/internal/schema.js.map +1 -0
- package/dist/internal/skills.js +28 -0
- package/dist/internal/skills.js.map +1 -0
- package/dist/internal/specialists.js +272 -0
- package/dist/internal/specialists.js.map +1 -0
- package/dist/internal/telemetry.js +85 -0
- package/dist/internal/telemetry.js.map +1 -0
- package/dist/internal/toolkit.d.ts +62 -0
- package/dist/internal/toolkit.d.ts.map +1 -0
- package/dist/internal/toolkit.js +108 -0
- package/dist/internal/toolkit.js.map +1 -0
- package/dist/internal/tools.js +113 -0
- package/dist/internal/tools.js.map +1 -0
- package/dist/internal/turns.d.ts +64 -0
- package/dist/internal/turns.d.ts.map +1 -0
- package/dist/internal/turns.js +214 -0
- package/dist/internal/turns.js.map +1 -0
- package/dist/internal/uuid.js +45 -0
- package/dist/internal/uuid.js.map +1 -0
- package/dist/internal/writer.d.ts +12 -0
- package/dist/internal/writer.d.ts.map +1 -0
- package/package.json +52 -3
- package/src/AppEvent.ts +134 -0
- package/src/Assistant.ts +589 -0
- package/src/AssistantActivity.ts +150 -0
- package/src/AssistantEngine.ts +343 -0
- package/src/AssistantEvent.ts +569 -0
- package/src/AssistantLoop.ts +209 -0
- package/src/AssistantToolCall.ts +40 -0
- package/src/Block.ts +84 -0
- package/src/BlockKit.ts +298 -0
- package/src/ChatId.ts +36 -0
- package/src/ChatLog.ts +247 -0
- package/src/ChatView.ts +587 -0
- package/src/Chats.ts +122 -0
- package/src/Compaction.ts +207 -0
- package/src/CurrentChat.ts +47 -0
- package/src/InputId.ts +43 -0
- package/src/Instructions.ts +72 -0
- package/src/Memory.ts +276 -0
- package/src/RunId.ts +46 -0
- package/src/Skill.ts +91 -0
- package/src/Specialist.ts +355 -0
- package/src/ToolDiscovery.ts +179 -0
- package/src/ToolFailure.ts +74 -0
- package/src/index.ts +120 -0
- package/src/internal/chat.ts +323 -0
- package/src/internal/events.ts +100 -0
- package/src/internal/history.ts +294 -0
- package/src/internal/instructions.ts +9 -0
- package/src/internal/loop.ts +274 -0
- package/src/internal/schema.ts +25 -0
- package/src/internal/skills.ts +37 -0
- package/src/internal/specialists.ts +410 -0
- package/src/internal/telemetry.ts +125 -0
- package/src/internal/toolkit.ts +188 -0
- package/src/internal/tools.ts +159 -0
- package/src/internal/turns.ts +410 -0
- package/src/internal/uuid.ts +50 -0
- package/src/internal/writer.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantLoop.d.ts","names":[],"sources":["../src/AssistantLoop.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAkCY,QAAQ;;;;;;;;;YAUR,cAAc;;;;;;;YAQd,aAAa;;;;;;;YAQb,KAAK,GAAG,KAAK,OAAU,GAAG;;;;;;;qBAQzB,cAAK;;;;;;;qBAQL,aAAI;;;;;;;qBAQJ,aAAI;;;;;;;;;;qBAWJ,aAAI;KAEZ,UAAU;;;;;;;;YASH,gBAAgB,UAAU,eAAe,KAAK,QAAQ,KAAK,YAAY,KAAK,aAAsB;;;;;;;iBAQ7F,YAAY,UAAU,eAAe,KAAK,OAAO;;WAEvD,uBAAuB;;;;;WAKvB,UAAU,QAAQ,QAAQ;;;;;;WAM1B,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;;;;;WAKvD,aAAa,cAAc;;;;;;;;iBASrB,QAAQ,UAAU,eAAe,KAAK,OAAO,iBAAiB,YAAY;;WAEhF;;;;;;;;;;;;;;;;;;qBA0BE,OAAQ,UAAU,eAAe,KAAK,OAAO,SAAS,WAAW,YAAY,YAAU,SACzF,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,gBAAgB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiC7F,MAAO,UAAU,eAAe,KAAK,OAAO,SAAO,SACrD,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB,gBAAgB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { empty, fromToolkit, merge } from "./internal/toolkit.js";
|
|
3
|
+
import { TurnTools, loop as loop$1, next as next$1, run as run$1, step as step$1, stop as stop$1, value as value$1 } from "./internal/loop.js";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Stream from "effect/Stream";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
//#region src/AssistantLoop.ts
|
|
8
|
+
var AssistantLoop_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
loop: () => loop,
|
|
10
|
+
next: () => next,
|
|
11
|
+
run: () => run,
|
|
12
|
+
step: () => step,
|
|
13
|
+
stop: () => stop,
|
|
14
|
+
value: () => value
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Emits a value from a loop body.
|
|
18
|
+
*
|
|
19
|
+
* @category constructors
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
*/
|
|
22
|
+
const value = value$1;
|
|
23
|
+
/**
|
|
24
|
+
* Ends a loop body's step and continues the loop with the given state.
|
|
25
|
+
*
|
|
26
|
+
* @category constructors
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
const next = next$1;
|
|
30
|
+
/**
|
|
31
|
+
* Ends the loop.
|
|
32
|
+
*
|
|
33
|
+
* @category constructors
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
*/
|
|
36
|
+
const stop = stop$1;
|
|
37
|
+
/**
|
|
38
|
+
* Runs a loop body from an initial state.
|
|
39
|
+
*
|
|
40
|
+
* It emits the body's values, and runs the body again with the state it passes to `next`, until
|
|
41
|
+
* the body calls `stop`.
|
|
42
|
+
*
|
|
43
|
+
* @category constructors
|
|
44
|
+
* @since 0.1.0
|
|
45
|
+
*/
|
|
46
|
+
const loop = loop$1;
|
|
47
|
+
const toolsOf = (toolkit) => Effect.map(TurnTools, (turn) => {
|
|
48
|
+
const own = toolkit === void 0 ? empty : fromToolkit(toolkit);
|
|
49
|
+
return Option.match(turn, {
|
|
50
|
+
onNone: () => own,
|
|
51
|
+
onSome: (builtins) => merge(builtins, own)
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Runs one step of the agent loop, as a body for {@link loop}.
|
|
56
|
+
*
|
|
57
|
+
* **Details**
|
|
58
|
+
*
|
|
59
|
+
* The step calls the model with the current history and emits its `ModelOutput`. It then runs
|
|
60
|
+
* the output's tool calls and emits a `ToolStarted` and a `ToolResult` for each. `then` receives
|
|
61
|
+
* the step's result and decides what comes next, with `AssistantLoop.next(state)` or
|
|
62
|
+
* `AssistantLoop.stop()`.
|
|
63
|
+
*
|
|
64
|
+
* A tool with Effect's `needsApproval` set fails the loop before the model is called: the
|
|
65
|
+
* library doesn't run calls that wait for approval (see the guide on involving the user).
|
|
66
|
+
*
|
|
67
|
+
* @category constructors
|
|
68
|
+
* @since 0.1.0
|
|
69
|
+
*/
|
|
70
|
+
const step = (options, then) => Stream.unwrap(Effect.map(toolsOf(options.toolkit), (toolkit) => step$1({
|
|
71
|
+
...options,
|
|
72
|
+
toolkit
|
|
73
|
+
}, then)));
|
|
74
|
+
/**
|
|
75
|
+
* Runs the agent loop as a stream.
|
|
76
|
+
*
|
|
77
|
+
* **Details**
|
|
78
|
+
*
|
|
79
|
+
* It runs {@link step} after step until the model is done and nothing new arrived while it
|
|
80
|
+
* worked, or until it has made `maxSteps` model calls.
|
|
81
|
+
*
|
|
82
|
+
* **Example** (A turn's loop with a step limit)
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* import { AssistantLoop } from "effect-assistant"
|
|
86
|
+
* import * as Layer from "effect/Layer"
|
|
87
|
+
* import * as Stream from "effect/Stream"
|
|
88
|
+
*
|
|
89
|
+
* const instructions = "You help a small marketing team."
|
|
90
|
+
*
|
|
91
|
+
* const MyAssistantLive = MyAssistant.toLayer((turn) =>
|
|
92
|
+
* AssistantLoop.run({
|
|
93
|
+
* instructions: turn.instructions(instructions),
|
|
94
|
+
* toolkit: MyTools, // the assistant's own tools are added to them
|
|
95
|
+
* history: turn.history(),
|
|
96
|
+
* maxSteps: 12
|
|
97
|
+
* }).pipe(Stream.concat(turn.replyIfOwed({ instructions })))
|
|
98
|
+
* ).pipe(Layer.provide(MyToolsLive))
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @category constructors
|
|
102
|
+
* @since 0.1.0
|
|
103
|
+
*/
|
|
104
|
+
const run = (options) => Stream.unwrap(Effect.map(toolsOf(options.toolkit), (toolkit) => run$1({
|
|
105
|
+
...options,
|
|
106
|
+
toolkit
|
|
107
|
+
})));
|
|
108
|
+
//#endregion
|
|
109
|
+
export { AssistantLoop_exports, loop, next, run, step, stop, value };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=AssistantLoop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantLoop.js","names":["Loop.value","Loop.next","Loop.stop","Loop.loop","Loop.TurnTools","Toolkits.empty","Toolkits.fromToolkit","Toolkits.merge","Loop.step","Loop.run"],"sources":["../src/AssistantLoop.ts"],"sourcesContent":["/**\n * Runs the agent loop, which calls the model, runs its tool calls, and repeats.\n *\n * Most handlers use `turn.loop` or `run.loop`. This module is for a loop of your own: {@link run}\n * runs the loop until the model is done, and {@link step} is one step, as a body for\n * {@link loop}.\n *\n * Tools are a native toolkit (`effect/ai/Toolkit`) whose handlers come from the handler's layer.\n * In a turn, the assistant's own tools (`send_user_message`, `wait`, and the specialist tools)\n * are added to them.\n *\n * @since 0.1.0\n */\nimport type * as AiError from \"effect/ai/AiError\"\nimport type * as LanguageModel from \"effect/ai/LanguageModel\"\nimport type * as Prompt from \"effect/ai/Prompt\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport type * as Toolkit from \"effect/ai/Toolkit\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Instructions from \"./Instructions.ts\"\nimport * as Loop from \"./internal/loop.ts\"\nimport * as Toolkits from \"./internal/toolkit.ts\"\n\n/**\n * Represents what the loop emits.\n *\n * For each model call, the loop emits the call's output. Then, for each of its tool calls, it\n * emits a `ToolStarted` before the call runs and the call's result.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Event = Loop.Event\n\n/**\n * Represents a chat's history as the model reads it.\n *\n * It holds the messages, and the last `seq` of the chat they cover.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ChatHistory = Loop.ChatHistory\n\n/**\n * Represents how a step went, for deciding what comes next.\n *\n * @category models\n * @since 0.1.0\n */\nexport type StepResult = Loop.StepResult\n\n/**\n * Represents what a loop body emits, which is either a value or what to do next.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Step<A, S> = Loop.Step<A, S>\n\n/**\n * Emits a value from a loop body.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const value = Loop.value\n\n/**\n * Ends a loop body's step and continues the loop with the given state.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const next = Loop.next\n\n/**\n * Ends the loop.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const stop = Loop.stop\n\n/**\n * Runs a loop body from an initial state.\n *\n * It emits the body's values, and runs the body again with the state it passes to `next`, until\n * the body calls `stop`.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const loop = Loop.loop\n\ntype NoTools = Record<never, never>\n\n/**\n * Extracts the services a toolkit's tools need when they run, which are their handlers and the\n * services those handlers use.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type ToolkitServices<T extends Record<string, Tool.Any>> = Tool.HandlersFor<T> | Toolkits.ToolServices<T>\n\n/**\n * Represents the options for {@link step}.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface StepOptions<T extends Record<string, Tool.Any> = NoTools> {\n /** The full system prompt, like `turn.instructions(text)`, optionally with span attributes. */\n readonly instructions: string | Instructions.Resolved\n /**\n * The tools the model can call, in addition to the assistant's own tools in a turn. Their\n * handlers come from the layer.\n */\n readonly toolkit?: Toolkit.Toolkit<T> | undefined\n /**\n * The current history, read at the start of the step. It is either messages, or a chat's\n * history (`turn.history()`), whose `throughSeq` the step's `ModelOutput` records as how far\n * the model had read.\n */\n readonly history: Effect.Effect<ReadonlyArray<Prompt.Message> | ChatHistory>\n /**\n * Defaults to `\"auto\"`, where a step without a tool call ends the loop. Forcing a call\n * (`\"required\"`, or a tool by name) is refused by newer models, and with extended thinking.\n */\n readonly toolChoice?: LanguageModel.ToolChoice<string> | undefined\n}\n\n/**\n * Represents the options for {@link run}.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Options<T extends Record<string, Tool.Any> = NoTools> extends StepOptions<T> {\n /** The model calls allowed. Defaults to 16. */\n readonly maxSteps?: number | undefined\n}\n\nconst toolsOf = <T extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<T> | undefined) =>\n Effect.map(Loop.TurnTools, (turn) => {\n const own = toolkit === undefined ? Toolkits.empty : Toolkits.fromToolkit(toolkit)\n\n return Option.match(turn, { onNone: () => own, onSome: (builtins) => Toolkits.merge(builtins, own) })\n })\n\n/**\n * Runs one step of the agent loop, as a body for {@link loop}.\n *\n * **Details**\n *\n * The step calls the model with the current history and emits its `ModelOutput`. It then runs\n * the output's tool calls and emits a `ToolStarted` and a `ToolResult` for each. `then` receives\n * the step's result and decides what comes next, with `AssistantLoop.next(state)` or\n * `AssistantLoop.stop()`.\n *\n * A tool with Effect's `needsApproval` set fails the loop before the model is called: the\n * library doesn't run calls that wait for approval (see the guide on involving the user).\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const step = <T extends Record<string, Tool.Any> = NoTools, S = never, E2 = never, R2 = never>(\n options: StepOptions<T>,\n then: (result: StepResult) => Stream.Stream<Step<never, S>, E2, R2>\n): Stream.Stream<Step<Event, S>, AiError.AiError | E2, LanguageModel.LanguageModel | ToolkitServices<T> | R2> =>\n Stream.unwrap(Effect.map(toolsOf(options.toolkit), (toolkit) => Loop.step({ ...options, toolkit }, then)))\n\n/**\n * Runs the agent loop as a stream.\n *\n * **Details**\n *\n * It runs {@link step} after step until the model is done and nothing new arrived while it\n * worked, or until it has made `maxSteps` model calls.\n *\n * **Example** (A turn's loop with a step limit)\n *\n * ```ts\n * import { AssistantLoop } from \"effect-assistant\"\n * import * as Layer from \"effect/Layer\"\n * import * as Stream from \"effect/Stream\"\n *\n * const instructions = \"You help a small marketing team.\"\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) =>\n * AssistantLoop.run({\n * instructions: turn.instructions(instructions),\n * toolkit: MyTools, // the assistant's own tools are added to them\n * history: turn.history(),\n * maxSteps: 12\n * }).pipe(Stream.concat(turn.replyIfOwed({ instructions })))\n * ).pipe(Layer.provide(MyToolsLive))\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const run = <T extends Record<string, Tool.Any> = NoTools>(\n options: Options<T>\n): Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | ToolkitServices<T>> =>\n Stream.unwrap(Effect.map(toolsOf(options.toolkit), (toolkit) => Loop.run({ ...options, toolkit })))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoEA,MAAa,QAAQA;;;;;;;AAQrB,MAAa,OAAOC;;;;;;;AAQpB,MAAa,OAAOC;;;;;;;;;;AAWpB,MAAa,OAAOC;AAmDpB,MAAM,WAA+C,YACnD,OAAO,IAAIC,YAAiB,SAAS;CACnC,MAAM,MAAM,YAAY,KAAA,IAAYC,QAAiBC,YAAqB,OAAO;CAEjF,OAAO,OAAO,MAAM,MAAM;EAAE,cAAc;EAAK,SAAS,aAAaC,MAAe,UAAU,GAAG;CAAE,CAAC;AACtG,CAAC;;;;;;;;;;;;;;;;;AAkBH,MAAa,QACX,SACA,SAEA,OAAO,OAAO,OAAO,IAAI,QAAQ,QAAQ,OAAO,IAAI,YAAYC,OAAU;CAAE,GAAG;CAAS;AAAQ,GAAG,IAAI,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC3G,MAAa,OACX,YAEA,OAAO,OAAO,OAAO,IAAI,QAAQ,QAAQ,OAAO,IAAI,YAAYC,MAAS;CAAE,GAAG;CAAS;AAAQ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
declare namespace AssistantToolCall_d_exports {
|
|
4
|
+
export { AssistantToolCall, idempotencyKey };
|
|
5
|
+
}
|
|
6
|
+
declare const AssistantToolCall_base: Context.ServiceClass<AssistantToolCall, "effect-assistant/AssistantToolCall", {
|
|
7
|
+
/** The call's id, as the model wrote it. It stays the same when a crash runs the call again. */
|
|
8
|
+
readonly callId: string;
|
|
9
|
+
/** The tool's name. */
|
|
10
|
+
readonly tool: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Service that describes the tool call a tool runs for.
|
|
14
|
+
*
|
|
15
|
+
* The agent loop provides it to every call.
|
|
16
|
+
*
|
|
17
|
+
* @category services
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
*/
|
|
20
|
+
export declare class AssistantToolCall extends AssistantToolCall_base {}
|
|
21
|
+
/**
|
|
22
|
+
* Gets an idempotency key for what the current tool call does outside the chat.
|
|
23
|
+
*
|
|
24
|
+
* The key is the same every time the call runs, and different for every other call.
|
|
25
|
+
*
|
|
26
|
+
* @category accessors
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
export declare const idempotencyKey: Effect.Effect<string, never, AssistantToolCall>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { AssistantToolCall_d_exports };
|
|
32
|
+
//# sourceMappingURL=AssistantToolCall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantToolCall.d.ts","names":[],"sources":["../src/AssistantToolCall.ts"],"mappings":";;;;;;;WAuBqB;;WAEF;;;;;;;;;;qBANN,0BAA0B;;;;;;;;;qBAkB1B,gBAAgB,OAAO,sBAAsB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
//#region src/AssistantToolCall.ts
|
|
4
|
+
/**
|
|
5
|
+
* Gives a tool the call it runs for.
|
|
6
|
+
*
|
|
7
|
+
* The {@link AssistantToolCall} service holds the call's id and the tool's name, and
|
|
8
|
+
* {@link idempotencyKey} derives a key for anything the call does outside the chat.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
var AssistantToolCall_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
AssistantToolCall: () => AssistantToolCall,
|
|
14
|
+
idempotencyKey: () => idempotencyKey
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Service that describes the tool call a tool runs for.
|
|
18
|
+
*
|
|
19
|
+
* The agent loop provides it to every call.
|
|
20
|
+
*
|
|
21
|
+
* @category services
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
*/
|
|
24
|
+
var AssistantToolCall = class extends Context.Service()("effect-assistant/AssistantToolCall") {};
|
|
25
|
+
/**
|
|
26
|
+
* Gets an idempotency key for what the current tool call does outside the chat.
|
|
27
|
+
*
|
|
28
|
+
* The key is the same every time the call runs, and different for every other call.
|
|
29
|
+
*
|
|
30
|
+
* @category accessors
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
*/
|
|
33
|
+
const idempotencyKey = AssistantToolCall.useSync((call) => `tool-call:${call.callId}`);
|
|
34
|
+
//#endregion
|
|
35
|
+
export { AssistantToolCall, AssistantToolCall_exports, idempotencyKey };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=AssistantToolCall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantToolCall.js","names":[],"sources":["../src/AssistantToolCall.ts"],"sourcesContent":["/**\n * Gives a tool the call it runs for.\n *\n * The {@link AssistantToolCall} service holds the call's id and the tool's name, and\n * {@link idempotencyKey} derives a key for anything the call does outside the chat.\n *\n * @since 0.1.0\n */\nimport * as Context from \"effect/Context\"\nimport type * as Effect from \"effect/Effect\"\n\n/**\n * Service that describes the tool call a tool runs for.\n *\n * The agent loop provides it to every call.\n *\n * @category services\n * @since 0.1.0\n */\nexport class AssistantToolCall extends Context.Service<\n AssistantToolCall,\n {\n /** The call's id, as the model wrote it. It stays the same when a crash runs the call again. */\n readonly callId: string\n /** The tool's name. */\n readonly tool: string\n }\n>()(\"effect-assistant/AssistantToolCall\") {}\n\n/**\n * Gets an idempotency key for what the current tool call does outside the chat.\n *\n * The key is the same every time the call runs, and different for every other call.\n *\n * @category accessors\n * @since 0.1.0\n */\nexport const idempotencyKey: Effect.Effect<string, never, AssistantToolCall> = AssistantToolCall.useSync(\n (call) => `tool-call:${call.callId}`\n)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,oBAAb,cAAuC,QAAQ,QAQ7C,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC;;;;;;;;;AAU3C,MAAa,iBAAkE,kBAAkB,SAC9F,SAAS,aAAa,KAAK,QAC9B"}
|
package/dist/Block.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
declare namespace Block_d_exports {
|
|
3
|
+
export { Any, Block, Codec, make };
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Schema that a block's props or answer can use.
|
|
7
|
+
*
|
|
8
|
+
* It is any codec that needs no services, since the client decodes and encodes with it too.
|
|
9
|
+
*
|
|
10
|
+
* @category utility types
|
|
11
|
+
* @since 0.1.0
|
|
12
|
+
*/
|
|
13
|
+
export type Codec = Schema.Codec<any, any>;
|
|
14
|
+
/**
|
|
15
|
+
* Represents a kind of UI block.
|
|
16
|
+
*
|
|
17
|
+
* @category models
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
*/
|
|
20
|
+
export interface Block<Kind extends string = string, Props extends Codec = Codec, Answer extends Codec = Codec> {
|
|
21
|
+
readonly kind: Kind;
|
|
22
|
+
/** When to use the block. The assistant sees this. */
|
|
23
|
+
readonly description: string;
|
|
24
|
+
/** The props the assistant fills in. */
|
|
25
|
+
readonly props: Props;
|
|
26
|
+
/** What using the block sends back, or `undefined` for a display-only card. */
|
|
27
|
+
readonly answer: Answer | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Represents any block, for generic constraints.
|
|
31
|
+
*
|
|
32
|
+
* @category utility types
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export type Any = Block;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a block.
|
|
38
|
+
*
|
|
39
|
+
* **Details**
|
|
40
|
+
*
|
|
41
|
+
* A block without an `answer` schema is a display-only card. Put the block in the assistant's
|
|
42
|
+
* `BlockKit` so the assistant can use it.
|
|
43
|
+
*
|
|
44
|
+
* **Example** (Defining a block with options to pick from)
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { Assistant, Block, BlockKit } from "effect-assistant"
|
|
48
|
+
* import * as Schema from "effect/Schema"
|
|
49
|
+
*
|
|
50
|
+
* const PickOne = Block.make("pick_one", {
|
|
51
|
+
* description: "Options to pick from, when the user chooses between a few.",
|
|
52
|
+
* props: Schema.Struct({ options: Schema.Array(Schema.String) }),
|
|
53
|
+
* answer: Schema.Struct({ picked: Schema.String })
|
|
54
|
+
* })
|
|
55
|
+
*
|
|
56
|
+
* export const MyAssistant = Assistant.make("my-assistant", { blocks: BlockKit.make([PickOne]) })
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @category constructors
|
|
60
|
+
* @since 0.1.0
|
|
61
|
+
*/
|
|
62
|
+
export declare const make: <const Kind extends string, Props extends Codec, Answer extends Codec = Schema.Never>(kind: Kind, options: {
|
|
63
|
+
readonly description: string;
|
|
64
|
+
readonly props: Props;
|
|
65
|
+
readonly answer?: Answer | undefined;
|
|
66
|
+
}) => Block<Kind, Props, Answer>;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { Block_d_exports };
|
|
69
|
+
//# sourceMappingURL=Block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Block.d.ts","names":[],"sources":["../src/Block.ts"],"mappings":";;;;;;;;;;;;YAmBY,QAAQ,OAAO;;;;;;;iBAQV,MAAM,8BAA8B,cAAc,QAAQ,OAAO,eAAe,QAAQ;WAC9F,MAAM;;WAEN;;WAEA,OAAO;;WAEP,QAAQ;;;;;;;;YASP,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4BL,aAAc,qBAAqB,cAAc,OAAO,eAAe,QAAQ,OAAO,OAAK,MAChG,MAAI;WAEC;WACA,OAAO;WACP,SAAS;MAEnB,MAAM,MAAM,OAAO"}
|
package/dist/Block.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/Block.ts
|
|
3
|
+
var Block_exports = /* @__PURE__ */ __exportAll({ make: () => make });
|
|
4
|
+
/**
|
|
5
|
+
* Creates a block.
|
|
6
|
+
*
|
|
7
|
+
* **Details**
|
|
8
|
+
*
|
|
9
|
+
* A block without an `answer` schema is a display-only card. Put the block in the assistant's
|
|
10
|
+
* `BlockKit` so the assistant can use it.
|
|
11
|
+
*
|
|
12
|
+
* **Example** (Defining a block with options to pick from)
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { Assistant, Block, BlockKit } from "effect-assistant"
|
|
16
|
+
* import * as Schema from "effect/Schema"
|
|
17
|
+
*
|
|
18
|
+
* const PickOne = Block.make("pick_one", {
|
|
19
|
+
* description: "Options to pick from, when the user chooses between a few.",
|
|
20
|
+
* props: Schema.Struct({ options: Schema.Array(Schema.String) }),
|
|
21
|
+
* answer: Schema.Struct({ picked: Schema.String })
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* export const MyAssistant = Assistant.make("my-assistant", { blocks: BlockKit.make([PickOne]) })
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @category constructors
|
|
28
|
+
* @since 0.1.0
|
|
29
|
+
*/
|
|
30
|
+
const make = (kind, options) => ({
|
|
31
|
+
kind,
|
|
32
|
+
description: options.description,
|
|
33
|
+
props: options.props,
|
|
34
|
+
answer: options.answer
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { Block_exports, make };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=Block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Block.js","names":[],"sources":["../src/Block.ts"],"sourcesContent":["/**\n * Defines UI blocks that an assistant can put in a message.\n *\n * A block is a bit of UI for when a structured answer is easier than typing, such as options to\n * pick from, a yes or no, or a small input. The assistant fills in its `props`, and\n * using the block sends back a value typed by its `answer` schema.\n *\n * @since 0.1.0\n */\nimport type * as Schema from \"effect/Schema\"\n\n/**\n * Schema that a block's props or answer can use.\n *\n * It is any codec that needs no services, since the client decodes and encodes with it too.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Codec = Schema.Codec<any, any>\n\n/**\n * Represents a kind of UI block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Block<Kind extends string = string, Props extends Codec = Codec, Answer extends Codec = Codec> {\n readonly kind: Kind\n /** When to use the block. The assistant sees this. */\n readonly description: string\n /** The props the assistant fills in. */\n readonly props: Props\n /** What using the block sends back, or `undefined` for a display-only card. */\n readonly answer: Answer | undefined\n}\n\n/**\n * Represents any block, for generic constraints.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = Block\n\n/**\n * Creates a block.\n *\n * **Details**\n *\n * A block without an `answer` schema is a display-only card. Put the block in the assistant's\n * `BlockKit` so the assistant can use it.\n *\n * **Example** (Defining a block with options to pick from)\n *\n * ```ts\n * import { Assistant, Block, BlockKit } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n *\n * const PickOne = Block.make(\"pick_one\", {\n * description: \"Options to pick from, when the user chooses between a few.\",\n * props: Schema.Struct({ options: Schema.Array(Schema.String) }),\n * answer: Schema.Struct({ picked: Schema.String })\n * })\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\", { blocks: BlockKit.make([PickOne]) })\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <const Kind extends string, Props extends Codec, Answer extends Codec = Schema.Never>(\n kind: Kind,\n options: {\n readonly description: string\n readonly props: Props\n readonly answer?: Answer | undefined\n }\n): Block<Kind, Props, Answer> => ({\n kind,\n description: options.description,\n props: options.props,\n answer: options.answer\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,MAAa,QACX,MACA,aAKgC;CAChC;CACA,aAAa,QAAQ;CACrB,OAAO,QAAQ;CACf,QAAQ,QAAQ;AAClB"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { AnyEntry, AssistantEvent_d_exports, BlockAnswer } from "./AssistantEvent.js";
|
|
2
|
+
import { Any, Block_d_exports } from "./Block.js";
|
|
3
|
+
import { AssistantMessage, ChatView_d_exports } from "./ChatView.js";
|
|
4
|
+
import { CurrentChat } from "./CurrentChat.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
declare namespace BlockKit_d_exports {
|
|
8
|
+
export { BlockKit, RenderArgs, RenderOptions, Renderers, State, apply, closeStale, fromEntries, make, staleBlocks, toClient };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Represents a catalog of blocks, with a lookup by kind.
|
|
12
|
+
*
|
|
13
|
+
* @category models
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
*/
|
|
16
|
+
export interface BlockKit<Blocks extends Any = Any> {
|
|
17
|
+
readonly blocks: ReadonlyArray<Blocks>;
|
|
18
|
+
readonly byKind: ReadonlyMap<string, Blocks>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a kit from blocks.
|
|
22
|
+
*
|
|
23
|
+
* @category constructors
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
*/
|
|
26
|
+
export declare const make: <const Blocks extends ReadonlyArray<Any>>(blocks: Blocks) => BlockKit<Blocks[number]>;
|
|
27
|
+
type KindOf<B> = B extends {
|
|
28
|
+
readonly kind: infer K extends string;
|
|
29
|
+
} ? K : never;
|
|
30
|
+
type BlockOf<Kit extends BlockKit, K> = Extract<Kit["blocks"][number], {
|
|
31
|
+
readonly kind: K;
|
|
32
|
+
}>;
|
|
33
|
+
type PropsOf<B> = B extends {
|
|
34
|
+
readonly props: Schema.Top;
|
|
35
|
+
} ? B["props"]["Type"] : never;
|
|
36
|
+
type AnswerOf<B> = B extends {
|
|
37
|
+
readonly answer: infer A;
|
|
38
|
+
} ? (A extends Schema.Top ? A["Type"] : never) : never;
|
|
39
|
+
/**
|
|
40
|
+
* Represents the state of a message's block.
|
|
41
|
+
*
|
|
42
|
+
* @category models
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
*/
|
|
45
|
+
export interface State {
|
|
46
|
+
readonly open: boolean;
|
|
47
|
+
/** The user's answer, if they used the block. */
|
|
48
|
+
readonly answer: BlockAnswer | undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns the block states after one more event.
|
|
52
|
+
*
|
|
53
|
+
* @category utils
|
|
54
|
+
* @since 0.1.0
|
|
55
|
+
*/
|
|
56
|
+
export declare const apply: (states: ReadonlyMap<string, State>, event: {
|
|
57
|
+
readonly _tag: string;
|
|
58
|
+
}) => ReadonlyMap<string, State>;
|
|
59
|
+
/**
|
|
60
|
+
* Returns the state of every message's block, by message id.
|
|
61
|
+
*
|
|
62
|
+
* @category utils
|
|
63
|
+
* @since 0.1.0
|
|
64
|
+
*/
|
|
65
|
+
export declare const fromEntries: (entries: ReadonlyArray<AnyEntry>) => ReadonlyMap<string, State>;
|
|
66
|
+
/**
|
|
67
|
+
* Returns the ids of the messages whose blocks are still open after the user typed the given
|
|
68
|
+
* number of messages without using them.
|
|
69
|
+
*
|
|
70
|
+
* **Details**
|
|
71
|
+
*
|
|
72
|
+
* These are the blocks {@link closeStale} closes.
|
|
73
|
+
*
|
|
74
|
+
* @category utils
|
|
75
|
+
* @since 0.1.0
|
|
76
|
+
*/
|
|
77
|
+
export declare const staleBlocks: (entries: ReadonlyArray<AnyEntry>, options: {
|
|
78
|
+
readonly messages: number;
|
|
79
|
+
}) => ReadonlyArray<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Closes the blocks the user moved on from, which are still open after the given number of typed
|
|
82
|
+
* messages.
|
|
83
|
+
*
|
|
84
|
+
* **Details**
|
|
85
|
+
*
|
|
86
|
+
* Run it from a turn, before or after its loop. It records a `BlocksClosed` event, so every
|
|
87
|
+
* client folds the blocks closed.
|
|
88
|
+
*
|
|
89
|
+
* @see {@link staleBlocks} for the blocks it closes
|
|
90
|
+
*
|
|
91
|
+
* @category utils
|
|
92
|
+
* @since 0.1.0
|
|
93
|
+
*/
|
|
94
|
+
export declare const closeStale: (options: {
|
|
95
|
+
readonly messages: number;
|
|
96
|
+
}) => Effect.Effect<void, never, CurrentChat>;
|
|
97
|
+
/**
|
|
98
|
+
* Represents what a renderer receives for one block.
|
|
99
|
+
*
|
|
100
|
+
* @category models
|
|
101
|
+
* @since 0.1.0
|
|
102
|
+
*/
|
|
103
|
+
export interface RenderArgs<B> {
|
|
104
|
+
/**
|
|
105
|
+
* The id of the message the block is on. A block whose button calls your API can key the work
|
|
106
|
+
* on it, with the chat's id, so the work runs once per block.
|
|
107
|
+
*/
|
|
108
|
+
readonly messageId: string;
|
|
109
|
+
readonly text: string;
|
|
110
|
+
readonly props: PropsOf<B>;
|
|
111
|
+
/**
|
|
112
|
+
* Whether the block is open. It is `false` once the block is answered, with the block or in
|
|
113
|
+
* words; show it read-only then.
|
|
114
|
+
*/
|
|
115
|
+
readonly open: boolean;
|
|
116
|
+
/** The user's answer, if they used the block. */
|
|
117
|
+
readonly answer: AnswerOf<B> | undefined;
|
|
118
|
+
/** Sends an answer. Only call it while `open`. */
|
|
119
|
+
readonly respond: (answer: AnswerOf<B>) => void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Represents a renderer for every kind in a kit.
|
|
123
|
+
*
|
|
124
|
+
* @category models
|
|
125
|
+
* @since 0.1.0
|
|
126
|
+
*/
|
|
127
|
+
export type Renderers<Kit extends BlockKit, Out> = { readonly [K in KindOf<Kit["blocks"][number]>]: (args: RenderArgs<BlockOf<Kit, K>>) => Out; };
|
|
128
|
+
/**
|
|
129
|
+
* Represents the options for rendering one message's block.
|
|
130
|
+
*
|
|
131
|
+
* @category models
|
|
132
|
+
* @since 0.1.0
|
|
133
|
+
*/
|
|
134
|
+
export interface RenderOptions {
|
|
135
|
+
/** Submits an answer, for example over RPC. */
|
|
136
|
+
readonly respond: (answer: BlockAnswer) => void;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Creates a client-side renderer for the blocks on a chat view's messages
|
|
140
|
+
* (`ChatView.AssistantMessage`).
|
|
141
|
+
*
|
|
142
|
+
* **Details**
|
|
143
|
+
*
|
|
144
|
+
* The renderers are type-checked, so every kind in the kit needs one, and each receives typed
|
|
145
|
+
* `props` and `answer`. A renderer's `respond` builds a `BlockAnswer` input, with the answer
|
|
146
|
+
* encoded by the block's answer schema, and passes it to the `respond` option to submit.
|
|
147
|
+
*
|
|
148
|
+
* The returned function returns `undefined` for a message without a block, or with a block it
|
|
149
|
+
* can't decode or has no renderer for.
|
|
150
|
+
*
|
|
151
|
+
* **Example** (Rendering blocks with React)
|
|
152
|
+
*
|
|
153
|
+
* ```tsx
|
|
154
|
+
* import { BlockKit } from "effect-assistant"
|
|
155
|
+
* import type { ReactNode } from "react"
|
|
156
|
+
*
|
|
157
|
+
* const Blocks = BlockKit.make([PickOne, OrderDraft])
|
|
158
|
+
*
|
|
159
|
+
* const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {
|
|
160
|
+
* pick_one: ({ props, open, respond }) =>
|
|
161
|
+
* props.options.map((option) => (
|
|
162
|
+
* <button key={option} disabled={!open} onClick={() => respond({ picked: option })}>
|
|
163
|
+
* {option}
|
|
164
|
+
* </button>
|
|
165
|
+
* )),
|
|
166
|
+
* // No answer schema: the button calls the app's own API with the props the user sees.
|
|
167
|
+
* order_draft: ({ props }) => <button onClick={() => placeOrder(props)}>Place order</button>
|
|
168
|
+
* })
|
|
169
|
+
*
|
|
170
|
+
* // For each AssistantMessage in the chat's view:
|
|
171
|
+
* renderBlock(message, { respond: (answer) => submit(answer) })
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @category constructors
|
|
175
|
+
* @since 0.1.0
|
|
176
|
+
*/
|
|
177
|
+
export declare function toClient<Kit extends BlockKit, Out>(kit: Kit, renderers: Renderers<Kit, Out>): (message: AssistantMessage, options: RenderOptions) => Out | undefined;
|
|
178
|
+
//#endregion
|
|
179
|
+
export { BlockKit_d_exports };
|
|
180
|
+
//# sourceMappingURL=BlockKit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockKit.d.ts","names":[],"sources":["../src/BlockKit.ts"],"mappings":";;;;;;;;;;;;;;;iBA0BiB,SAAS,eAAe,MAAY;WAC1C,QAAQ,cAAc;WACtB,QAAQ,oBAAoB;;;;;;;;qBAS1B,aAAc,eAAe,cAAc,MAAU,QAAU,WAAS,SAAS;KAKzF,OAAO,KAAK;WAAqB,YAAY;IAAqB;KAElE,QAAQ,YAAY,UAAU,KAAK,QAAQ;WAAkC,MAAM;;KAEnF,QAAQ,KAAK;WAAqB,OAAO,OAAO;IAAQ;KAExD,SAAS,KAAK;WAAqB,cAAc;KAAO,UAAU,OAAO,MAAM;;;;;;;iBAQnE;WACN;;WAEA,QAAQ;;;;;;;;qBASN,QAAK,QACR,oBAAoB,QAAM;WAChB;MACjB,oBAAoB;;;;;;;qBAkCV,cAAW,SAAa,cAAc,cAA2B,oBAAoB;;;;;;;;;;;;qBAmBrF,cAAW,SACb,cAAc,WAAwB;WAC3B;MACnB;;;;;;;;;;;;;;;qBA+BU,aAAU;WAAsD;MAK3E,OAAA,oBAAA;;;;;;;iBAQe,WAAW;;;;;WAKjB;WACA;WACA,OAAO,QAAQ;;;;;WAKf;;WAEA,QAAQ,SAAS;;WAEjB,UAAU,QAAQ,SAAS;;;;;;;;YAS1B,UAAU,YAAY,UAAU,mBAChC,KAAK,OAAO,0BAA0B,MAAM,WAAW,QAAQ,KAAK,QAAQ;;;;;;;iBASvE;;WAEN,UAAU,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA0Cb,SAAS,YAAY,UAAU,KAC7C,KAAK,KACL,WAAW,UAAU,KAAK,QACxB,SAAS,kBAA2B,SAAS,kBAAkB"}
|
package/dist/BlockKit.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { fromKey } from "./InputId.js";
|
|
3
|
+
import { BlockAnswer, BlocksClosed, isLibraryEvent } from "./AssistantEvent.js";
|
|
4
|
+
import { CurrentChat } from "./CurrentChat.js";
|
|
5
|
+
import * as Predicate from "effect/Predicate";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
//#region src/BlockKit.ts
|
|
10
|
+
/**
|
|
11
|
+
* Groups the blocks an assistant may put in its messages, and renders them on the client.
|
|
12
|
+
*
|
|
13
|
+
* A kit is created with {@link make} and passed to `Assistant.make`. {@link fromEntries} computes
|
|
14
|
+
* the state of every message's block from a chat's log, {@link closeStale} closes the blocks the
|
|
15
|
+
* user moved on from, and {@link toClient} creates a client-side renderer with one function per
|
|
16
|
+
* block kind.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
*/
|
|
20
|
+
var BlockKit_exports = /* @__PURE__ */ __exportAll({
|
|
21
|
+
apply: () => apply,
|
|
22
|
+
closeStale: () => closeStale,
|
|
23
|
+
fromEntries: () => fromEntries,
|
|
24
|
+
make: () => make,
|
|
25
|
+
staleBlocks: () => staleBlocks,
|
|
26
|
+
toClient: () => toClient
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Creates a kit from blocks.
|
|
30
|
+
*
|
|
31
|
+
* @category constructors
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
*/
|
|
34
|
+
const make = (blocks) => ({
|
|
35
|
+
blocks,
|
|
36
|
+
byKind: new Map(blocks.map((block) => [block.kind, block]))
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Returns the block states after one more event.
|
|
40
|
+
*
|
|
41
|
+
* @category utils
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
*/
|
|
44
|
+
const apply = (states, event) => {
|
|
45
|
+
if (!isLibraryEvent(event)) return states;
|
|
46
|
+
const close = (ids) => {
|
|
47
|
+
const open = ids.filter((id) => states.get(id)?.open === true);
|
|
48
|
+
if (open.length === 0) return states;
|
|
49
|
+
const next = new Map(states);
|
|
50
|
+
for (const id of open) next.set(id, {
|
|
51
|
+
open: false,
|
|
52
|
+
answer: void 0
|
|
53
|
+
});
|
|
54
|
+
return next;
|
|
55
|
+
};
|
|
56
|
+
if (Predicate.isTagged(event, "BlocksClosed")) return close(event.messageIds);
|
|
57
|
+
if (Predicate.isTagged(event, "AssistantMessage")) {
|
|
58
|
+
const closed = close(event.closes ?? []);
|
|
59
|
+
return event.block === void 0 ? closed : new Map(closed).set(event.id, {
|
|
60
|
+
open: true,
|
|
61
|
+
answer: void 0
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (!Predicate.isTagged(event, "BlockAnswer") || states.get(event.messageId)?.open !== true) return states;
|
|
65
|
+
return new Map(states).set(event.messageId, {
|
|
66
|
+
open: false,
|
|
67
|
+
answer: event
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Returns the state of every message's block, by message id.
|
|
72
|
+
*
|
|
73
|
+
* @category utils
|
|
74
|
+
* @since 0.1.0
|
|
75
|
+
*/
|
|
76
|
+
const fromEntries = (entries) => {
|
|
77
|
+
let states = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const entry of entries) states = apply(states, entry.event);
|
|
79
|
+
return states;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Returns the ids of the messages whose blocks are still open after the user typed the given
|
|
83
|
+
* number of messages without using them.
|
|
84
|
+
*
|
|
85
|
+
* **Details**
|
|
86
|
+
*
|
|
87
|
+
* These are the blocks {@link closeStale} closes.
|
|
88
|
+
*
|
|
89
|
+
* @category utils
|
|
90
|
+
* @since 0.1.0
|
|
91
|
+
*/
|
|
92
|
+
const staleBlocks = (entries, options) => {
|
|
93
|
+
return [...fromEntries(entries)].flatMap(([id, state]) => state.open ? [id] : []).filter((id) => {
|
|
94
|
+
const shown = entries.findIndex(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "AssistantMessage") && event.id === id);
|
|
95
|
+
if (shown === -1) return false;
|
|
96
|
+
return entries.slice(shown + 1).filter(({ event }) => Predicate.isTagged(event, "UserMessage")).length >= options.messages;
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Closes the blocks the user moved on from, which are still open after the given number of typed
|
|
101
|
+
* messages.
|
|
102
|
+
*
|
|
103
|
+
* **Details**
|
|
104
|
+
*
|
|
105
|
+
* Run it from a turn, before or after its loop. It records a `BlocksClosed` event, so every
|
|
106
|
+
* client folds the blocks closed.
|
|
107
|
+
*
|
|
108
|
+
* @see {@link staleBlocks} for the blocks it closes
|
|
109
|
+
*
|
|
110
|
+
* @category utils
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
*/
|
|
113
|
+
const closeStale = Effect.fn(function* (options) {
|
|
114
|
+
const chat = yield* CurrentChat;
|
|
115
|
+
const stale = staleBlocks(yield* chat.entries, options);
|
|
116
|
+
if (stale.length > 0) yield* chat.append(BlocksClosed.make({ messageIds: stale }));
|
|
117
|
+
});
|
|
118
|
+
function toClient(kit, renderers) {
|
|
119
|
+
return (message, options) => {
|
|
120
|
+
if (message.block === void 0) return void 0;
|
|
121
|
+
const { kind } = message.block;
|
|
122
|
+
const block = kit.byKind.get(kind);
|
|
123
|
+
const render = renderers[kind];
|
|
124
|
+
if (block === void 0 || render === void 0) return void 0;
|
|
125
|
+
const props = Schema.decodeUnknownOption(block.props)(message.block.props);
|
|
126
|
+
if (Option.isNone(props)) return void 0;
|
|
127
|
+
const given = message.block.answer;
|
|
128
|
+
const answerCodec = block.answer === void 0 ? void 0 : Schema.toCodecJson(block.answer);
|
|
129
|
+
const answer = given === void 0 || answerCodec === void 0 ? Option.none() : Schema.decodeUnknownOption(answerCodec)(given);
|
|
130
|
+
return render({
|
|
131
|
+
messageId: message.id,
|
|
132
|
+
text: message.text,
|
|
133
|
+
props: props.value,
|
|
134
|
+
open: message.block.status === "open",
|
|
135
|
+
answer: Option.getOrUndefined(answer),
|
|
136
|
+
respond: (value) => options.respond(BlockAnswer.make({
|
|
137
|
+
id: fromKey(`${message.id}:answer`),
|
|
138
|
+
messageId: message.id,
|
|
139
|
+
kind,
|
|
140
|
+
value: answerCodec === void 0 ? value : Schema.encodeUnknownSync(answerCodec)(value)
|
|
141
|
+
}))
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
export { BlockKit_exports, apply, closeStale, fromEntries, make, staleBlocks, toClient };
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=BlockKit.js.map
|