effect-assistant 0.4.0 → 0.6.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/dist/Assistant.d.ts +10 -10
- package/dist/Assistant.d.ts.map +1 -1
- package/dist/Assistant.js +1 -1
- package/dist/Assistant.js.map +1 -1
- package/dist/AssistantActivity.d.ts +2 -2
- package/dist/AssistantActivity.d.ts.map +1 -1
- package/dist/AssistantActivity.js +5 -5
- package/dist/AssistantActivity.js.map +1 -1
- package/dist/AssistantEngine.d.ts +3 -3
- package/dist/AssistantEngine.js +2 -2
- package/dist/AssistantEngine.js.map +1 -1
- package/dist/AssistantEvent.d.ts +19 -8
- package/dist/AssistantEvent.d.ts.map +1 -1
- package/dist/AssistantEvent.js +20 -9
- package/dist/AssistantEvent.js.map +1 -1
- package/dist/AssistantLoop.d.ts +2 -2
- package/dist/AssistantLoop.js.map +1 -1
- package/dist/Block.d.ts +63 -1
- package/dist/Block.d.ts.map +1 -1
- package/dist/Block.js +63 -2
- package/dist/Block.js.map +1 -1
- package/dist/BlockKit.d.ts +25 -3
- package/dist/BlockKit.d.ts.map +1 -1
- package/dist/BlockKit.js +14 -12
- package/dist/BlockKit.js.map +1 -1
- package/dist/ChatLog.d.ts +9 -9
- package/dist/ChatLog.d.ts.map +1 -1
- package/dist/ChatLog.js +21 -21
- package/dist/ChatLog.js.map +1 -1
- package/dist/Compaction.d.ts +10 -10
- package/dist/Compaction.d.ts.map +1 -1
- package/dist/Compaction.js +19 -19
- package/dist/Compaction.js.map +1 -1
- package/dist/Memory.d.ts +1 -1
- package/dist/Memory.js +1 -1
- package/dist/Memory.js.map +1 -1
- package/dist/Skill.d.ts +1 -1
- package/dist/Skill.js +1 -1
- package/dist/Skill.js.map +1 -1
- package/dist/Specialist.d.ts +1 -1
- package/dist/Specialist.js +1 -1
- package/dist/Specialist.js.map +1 -1
- package/dist/Timeline.d.ts +13 -9
- package/dist/Timeline.d.ts.map +1 -1
- package/dist/Timeline.js +35 -23
- package/dist/Timeline.js.map +1 -1
- package/dist/ToolApproval.d.ts +1 -1
- package/dist/ToolApproval.js +1 -1
- package/dist/ToolApproval.js.map +1 -1
- package/dist/ToolDiscovery.d.ts +1 -1
- package/dist/ToolDiscovery.js +1 -1
- package/dist/ToolDiscovery.js.map +1 -1
- package/dist/ToolResult.d.ts +56 -0
- package/dist/ToolResult.d.ts.map +1 -0
- package/dist/ToolResult.js +49 -0
- package/dist/ToolResult.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -3
- package/dist/internal/chat.js +4 -4
- package/dist/internal/chat.js.map +1 -1
- package/dist/internal/events.js +1 -1
- package/dist/internal/events.js.map +1 -1
- package/dist/internal/history.js +23 -23
- package/dist/internal/history.js.map +1 -1
- package/dist/internal/loop.d.ts +1 -1
- package/dist/internal/loop.d.ts.map +1 -1
- package/dist/internal/loop.js +12 -6
- package/dist/internal/loop.js.map +1 -1
- package/dist/internal/toolkit.d.ts +7 -1
- package/dist/internal/toolkit.d.ts.map +1 -1
- package/dist/internal/toolkit.js +47 -4
- package/dist/internal/toolkit.js.map +1 -1
- package/dist/internal/tools.js +39 -6
- package/dist/internal/tools.js.map +1 -1
- package/dist/internal/turns.d.ts.map +1 -1
- package/dist/internal/turns.js +24 -21
- package/dist/internal/turns.js.map +1 -1
- package/package.json +1 -1
- package/src/Assistant.ts +11 -11
- package/src/AssistantActivity.ts +9 -7
- package/src/AssistantEngine.ts +3 -3
- package/src/AssistantEvent.ts +21 -10
- package/src/AssistantLoop.ts +3 -3
- package/src/Block.ts +82 -1
- package/src/BlockKit.ts +37 -4
- package/src/ChatLog.ts +31 -26
- package/src/Compaction.ts +21 -21
- package/src/Memory.ts +1 -1
- package/src/Skill.ts +1 -1
- package/src/Specialist.ts +1 -1
- package/src/Timeline.ts +38 -25
- package/src/ToolApproval.ts +1 -1
- package/src/ToolDiscovery.ts +1 -1
- package/src/ToolResult.ts +80 -0
- package/src/index.ts +5 -0
- package/src/internal/chat.ts +4 -4
- package/src/internal/events.ts +1 -1
- package/src/internal/history.ts +32 -28
- package/src/internal/loop.ts +24 -7
- package/src/internal/toolkit.ts +55 -5
- package/src/internal/tools.ts +81 -5
- package/src/internal/turns.ts +35 -29
package/dist/AssistantEvent.js
CHANGED
|
@@ -205,12 +205,16 @@ const UserInput = Schema.Union([
|
|
|
205
205
|
* Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its
|
|
206
206
|
* props.
|
|
207
207
|
*
|
|
208
|
+
* A block that shows a tool's result (`Block.fromToolResult`) has the tool's name as its kind and
|
|
209
|
+
* the result's `resultId`.
|
|
210
|
+
*
|
|
208
211
|
* @category schemas
|
|
209
212
|
* @since 0.1.0
|
|
210
213
|
*/
|
|
211
214
|
var MessageBlock = class extends Schema.Class("effect-assistant/MessageBlock")({
|
|
212
215
|
kind: Schema.String,
|
|
213
|
-
props: Schema.Unknown
|
|
216
|
+
props: Schema.Unknown,
|
|
217
|
+
resultId: Schema.optional(Schema.String)
|
|
214
218
|
}) {};
|
|
215
219
|
/**
|
|
216
220
|
* Represents a message the assistant sent the user with `send_user_message`, optionally with a
|
|
@@ -268,7 +272,7 @@ var ModelOutput = class extends Schema.TaggedClass()("ModelOutput", {
|
|
|
268
272
|
* arrived while the model was working. A turn has handled what its
|
|
269
273
|
* outputs had read.
|
|
270
274
|
*/
|
|
271
|
-
|
|
275
|
+
seenThroughSequence: Schema.optional(Schema.Int),
|
|
272
276
|
/** The model that wrote it, as the provider's id for it or the name the context gave it. */
|
|
273
277
|
model: Schema.optional(Schema.String),
|
|
274
278
|
/** The provider's id for the response, when it gives one. */
|
|
@@ -306,6 +310,10 @@ const ToolOutcome = Schema.Literals(["success", "failure"]);
|
|
|
306
310
|
* failure includes its reason when it has one, which is a `ToolFailure`'s `reason` or the
|
|
307
311
|
* failure's tag.
|
|
308
312
|
*
|
|
313
|
+
* When the model reads less than the whole success (a `ToolResult.modelView`, or the `resultId`
|
|
314
|
+
* added for a tool the assistant's blocks show), `full` holds the whole success, encoded by the
|
|
315
|
+
* tool's success schema.
|
|
316
|
+
*
|
|
309
317
|
* @category schemas
|
|
310
318
|
* @since 0.1.0
|
|
311
319
|
*/
|
|
@@ -314,7 +322,10 @@ var ToolResult = class extends Schema.TaggedClass()("ToolResult", {
|
|
|
314
322
|
tool: Schema.String,
|
|
315
323
|
outcome: ToolOutcome,
|
|
316
324
|
result: Schema.Json,
|
|
317
|
-
reason: Schema.optional(Schema.String)
|
|
325
|
+
reason: Schema.optional(Schema.String),
|
|
326
|
+
full: Schema.optional(Schema.Json),
|
|
327
|
+
/** The id the model passes in a `tool_result` block to show this result. */
|
|
328
|
+
resultId: Schema.optional(Schema.String)
|
|
318
329
|
}) {};
|
|
319
330
|
/**
|
|
320
331
|
* Represents the start of a turn.
|
|
@@ -333,23 +344,23 @@ var TurnStarted = class extends Schema.TaggedClass()("TurnStarted", {
|
|
|
333
344
|
/**
|
|
334
345
|
* Represents a turn that finished.
|
|
335
346
|
*
|
|
336
|
-
* `
|
|
347
|
+
* `throughSequence` is the last entry the turn read. Inputs after it still need a turn.
|
|
337
348
|
*
|
|
338
349
|
* @category schemas
|
|
339
350
|
* @since 0.1.0
|
|
340
351
|
*/
|
|
341
|
-
var TurnCompleted = class extends Schema.TaggedClass()("TurnCompleted", {
|
|
352
|
+
var TurnCompleted = class extends Schema.TaggedClass()("TurnCompleted", { throughSequence: Schema.Int }) {};
|
|
342
353
|
/**
|
|
343
354
|
* Represents a turn that failed.
|
|
344
355
|
*
|
|
345
|
-
* `
|
|
356
|
+
* `throughSequence` covers everything stored when the turn failed, so a failing input isn't retried
|
|
346
357
|
* forever.
|
|
347
358
|
*
|
|
348
359
|
* @category schemas
|
|
349
360
|
* @since 0.1.0
|
|
350
361
|
*/
|
|
351
362
|
var TurnFailed = class extends Schema.TaggedClass()("TurnFailed", {
|
|
352
|
-
|
|
363
|
+
throughSequence: Schema.Int,
|
|
353
364
|
message: Schema.String
|
|
354
365
|
}) {};
|
|
355
366
|
/**
|
|
@@ -490,7 +501,7 @@ const isAppInput = (event) => !isLibraryEvent(event) && Predicate.hasProperty(ev
|
|
|
490
501
|
/**
|
|
491
502
|
* Schema for a stored event with its position in the chat.
|
|
492
503
|
*
|
|
493
|
-
* `
|
|
504
|
+
* `sequence` starts at 1 and has no gaps. `at` is the time the event was stored, in epoch
|
|
494
505
|
* milliseconds.
|
|
495
506
|
*
|
|
496
507
|
* @category schemas
|
|
@@ -498,7 +509,7 @@ const isAppInput = (event) => !isLibraryEvent(event) && Predicate.hasProperty(ev
|
|
|
498
509
|
*/
|
|
499
510
|
const Entry = Schema.Struct({
|
|
500
511
|
chatId: ChatId,
|
|
501
|
-
|
|
512
|
+
sequence: Schema.Int,
|
|
502
513
|
at: Schema.Finite,
|
|
503
514
|
event: Schema.Union([AssistantEvent, StoredAppEvent])
|
|
504
515
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantEvent.js","names":["makeInputId"],"sources":["../src/AssistantEvent.ts"],"sourcesContent":["/**\n * Defines the events that can happen in a chat.\n *\n * A chat is an append-only log of these events, and the log is the source of truth. A chat client\n * receives the part of the log its user sees (its `Timeline`), and the model's history is rebuilt\n * from the log before every model call. Besides the library's events ({@link AssistantEvent}), a\n * log holds the application's own events ({@link StoredAppEvent}).\n *\n * @since 0.1.0\n */\nimport * as Response from \"effect/ai/Response\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as SchemaAST from \"effect/SchemaAST\"\nimport { ChatId } from \"./ChatId.ts\"\nimport { InputId, make as makeInputId } from \"./InputId.ts\"\nimport { inputKey } from \"./internal/input.ts\"\nimport { RunId } from \"./RunId.ts\"\n\n/**\n * Schema for a file the user attached to a message, kept by reference.\n *\n * The file itself is stored elsewhere, so the log never holds its bytes.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Attachment = Schema.Struct({\n fileId: Schema.String.check(Schema.isMaxLength(64)),\n name: Schema.String.check(Schema.isMaxLength(255)),\n mediaType: Schema.String.check(Schema.isMaxLength(127)),\n /** In bytes. */\n size: Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))\n})\n\n/**\n * The maximum number of files one message can carry.\n *\n * Each attached file is a line in every prompt after the message.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const maxAttachments = 10\n\n/**\n * Represents a file the user attached to a message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Attachment = typeof Attachment.Type\n\n/**\n * Formats a byte count for people and the model, such as `2.1 MB`.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const formatSize = (bytes: number): string =>\n bytes < 1024\n ? `${bytes} B`\n : bytes < 1024 * 1024\n ? `${(bytes / 1024).toFixed(1)} KB`\n : bytes < 1024 * 1024 * 1024\n ? `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n : `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n\n/**\n * Returns the line the model reads for an attachment.\n *\n * The line names the file and never holds its content.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const attachmentLine = (attachment: Attachment): string =>\n `[attached: ${attachment.name}, ${attachment.mediaType}, ${formatSize(attachment.size)}, id ${attachment.fileId}]`\n\n/**\n * Returns a message's text followed by a line for each attached file.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const textWithAttachments = (text: string, attachments: ReadonlyArray<Attachment> | undefined): string =>\n attachments === undefined || attachments.length === 0\n ? text\n : [text, ...attachments.map(attachmentLine)].filter((part) => part.length > 0).join(\"\\n\")\n\n/**\n * Represents text the user typed, and the files they attached.\n *\n * `id` defaults to a new one. Pass the client's own id so that a retried send stores the message\n * once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class UserMessage extends Schema.TaggedClass<UserMessage>()(\"UserMessage\", {\n id: InputId.pipe(Schema.withConstructorDefault(makeInputId())),\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(Attachment).check(Schema.isMaxLength(maxAttachments)))\n}) {}\n\n/**\n * Represents the user's answer to a block.\n *\n * It holds the id of the message the block was on, and the value in the block's answer schema\n * (in its JSON form). A chat UI shows it inside the block rather than as a message.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlockAnswer extends Schema.TaggedClass<BlockAnswer>()(\"BlockAnswer\", {\n id: InputId,\n messageId: Schema.String,\n kind: Schema.String,\n value: Schema.Unknown\n}) {}\n\n/**\n * Represents a specialist run that finished working on a brief.\n *\n * Unless the assistant stopped the run, it is an input, and a turn reads its report and decides\n * what to tell the user. Its id is fixed per run and brief, so it is stored once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistFinished extends Schema.TaggedClass<SpecialistFinished>()(\"SpecialistFinished\", {\n id: InputId,\n runId: RunId,\n specialist: Schema.String,\n /** Which brief this answers: 1 for the first, then one more per follow-up. */\n brief: Schema.Int,\n goal: Schema.String,\n status: Schema.Literals([\"answered\", \"failed\", \"stopped\", \"cut_off\"]),\n report: Schema.optional(Schema.String),\n error: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the user's decision on a tool call that waits for their approval: `\"approved\"`, so\n * the call runs, or `\"rejected\"`, with an optional short reason the model reads.\n *\n * Its id is fixed per call (see `ToolApproval.approve`), so a decision is stored once. A decision on\n * a call that isn't waiting any more (decided, or expired) isn't stored.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalDecision extends Schema.TaggedClass<ApprovalDecision>()(\"ApprovalDecision\", {\n id: InputId,\n callId: Schema.String,\n decision: Schema.Literals([\"approved\", \"rejected\"]),\n reason: Schema.optional(Schema.String.check(Schema.isMaxLength(500)))\n}) {}\n\n/**\n * Schema for every input `Assistant.submit` accepts.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Input = Schema.Union([UserMessage, BlockAnswer, SpecialistFinished, ApprovalDecision])\n\n/**\n * Represents an input to an assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Schema for the inputs from the chat's user, which are messages, block answers, and decisions on\n * tool calls that wait for their approval.\n *\n * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's\n * events come from the server.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserInput = Schema.Union([UserMessage, BlockAnswer, ApprovalDecision])\n\n/**\n * Represents an input from the chat's user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserInput = typeof UserInput.Type\n\n/**\n * Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its\n * props.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class MessageBlock extends Schema.Class<MessageBlock>(\"effect-assistant/MessageBlock\")({\n kind: Schema.String,\n props: Schema.Unknown\n}) {}\n\n/**\n * Represents a message the assistant sent the user with `send_user_message`, optionally with a\n * block.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class AssistantMessage extends Schema.TaggedClass<AssistantMessage>()(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n block: Schema.optional(MessageBlock),\n /** Earlier messages whose block the user answered by typing instead. Their blocks close. */\n closes: Schema.optional(Schema.Array(Schema.String))\n}) {}\n\n/**\n * Schema for a tool call the model made.\n *\n * It is `effect/ai`'s tool call part (`Response.ToolCallPart`) for any tool, with its params in\n * JSON, as the model wrote them.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolCallPart = Schema.Struct({\n ...Response.ToolCallPart(\"\", Schema.Json).fields,\n name: Schema.String\n}).annotate({ identifier: \"ToolCallPart\" })\n\n/**\n * Represents a tool call the model made.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolCallPart = typeof ToolCallPart.Type\n\n/**\n * Schema for what the log keeps of a model call.\n *\n * It uses `effect/ai`'s response parts for text, reasoning (with the provider's metadata, such as\n * a thinking block's signature), and tool calls.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ModelPart = Schema.Union([Response.TextPart, Response.ReasoningPart, ToolCallPart])\n\n/**\n * Represents a part of a model call's output.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ModelPart = typeof ModelPart.Type\n\n/**\n * Represents what one model call wrote, as response parts for text, reasoning, and tool calls.\n *\n * `Prompt.fromResponseParts` turns the parts into history.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ModelOutput extends Schema.TaggedClass<ModelOutput>()(\"ModelOutput\", {\n parts: Schema.Array(ModelPart),\n /**\n * The last entry the model had read when it wrote this. Inputs after it\n * arrived while the model was working. A turn has handled what its\n * outputs had read.\n */\n seenThroughSeq: Schema.optional(Schema.Int),\n /** The model that wrote it, as the provider's id for it or the name the context gave it. */\n model: Schema.optional(Schema.String),\n /** The provider's id for the response, when it gives one. */\n responseId: Schema.optional(Schema.String),\n /** The call's token usage, with input (including the cache's reads and writes) and output. */\n usage: Schema.optional(Response.Usage),\n /** How long the call took, in milliseconds. */\n durationMillis: Schema.optional(Schema.Finite)\n}) {}\n\n/**\n * Represents a tool call that is about to run.\n *\n * It is stored before the tool runs, so after a crash the log says which calls may have done\n * something. Those calls run again with the same call id (see `AssistantToolCall`).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolStarted extends Schema.TaggedClass<ToolStarted>()(\"ToolStarted\", {\n callId: Schema.String,\n tool: Schema.String\n}) {}\n\n/**\n * Schema for how a tool call went, either `\"success\"` or `\"failure\"`, which the model reads as an\n * error.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolOutcome = Schema.Literals([\"success\", \"failure\"])\n\n/**\n * Represents how a tool call went.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolOutcome = typeof ToolOutcome.Type\n\n/**\n * Represents the result of one tool call, as the model reads it.\n *\n * It holds the tool's success or failure in its encoded (JSON) form, and which of the two it is. A\n * failure includes its reason when it has one, which is a `ToolFailure`'s `reason` or the\n * failure's tag.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolResult extends Schema.TaggedClass<ToolResult>()(\"ToolResult\", {\n callId: Schema.String,\n tool: Schema.String,\n outcome: ToolOutcome,\n result: Schema.Json,\n reason: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the start of a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnStarted extends Schema.TaggedClass<TurnStarted>()(\"TurnStarted\", {\n /** The assistant that runs it. */\n assistant: Schema.String,\n /**\n * The id of the turn's trace, when a tracer records it (not Effect's own, which exports nothing).\n */\n traceId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents a turn that finished.\n *\n * `throughSeq` is the last entry the turn read. Inputs after it still need a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnCompleted extends Schema.TaggedClass<TurnCompleted>()(\"TurnCompleted\", {\n throughSeq: Schema.Int\n}) {}\n\n/**\n * Represents a turn that failed.\n *\n * `throughSeq` covers everything stored when the turn failed, so a failing input isn't retried\n * forever.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnFailed extends Schema.TaggedClass<TurnFailed>()(\"TurnFailed\", {\n throughSeq: Schema.Int,\n message: Schema.String\n}) {}\n\n/**\n * Represents a specialist run that started working, either spawned or continued by a follow-up\n * message.\n *\n * The model doesn't see it, since the call that started the run says so, and the run's report\n * arrives as a {@link SpecialistFinished}.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistStarted extends Schema.TaggedClass<SpecialistStarted>()(\"SpecialistStarted\", {\n runId: RunId,\n specialist: Schema.String,\n /** The goal it's working on. */\n goal: Schema.String\n}) {}\n\n/**\n * Represents the deletion of a chat.\n *\n * The chat stores nothing after it, the work running in the chat stopped, and its subscribers'\n * streams end here. Its log stays.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ChatDeleted extends Schema.TaggedClass<ChatDeleted>()(\"ChatDeleted\", {}) {}\n\n/**\n * Represents blocks that closed without an answer, like the ones `BlockKit.closeStale` closes\n * from a turn once the user moved on.\n *\n * The model doesn't see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlocksClosed extends Schema.TaggedClass<BlocksClosed>()(\"BlocksClosed\", {\n messageIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Represents a tool call that waits for the user's approval instead of running: its tool needs\n * approval (Effect's `needsApproval`).\n *\n * **Details**\n *\n * It holds the call's id, the tool, and its params, in JSON, as the model wrote them. The model\n * reads it as the call's result, saying it waits for the user; the user's\n * {@link ApprovalDecision} decides it, and an approved call runs then, with the same id.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalRequested extends Schema.TaggedClass<ApprovalRequested>()(\"ApprovalRequested\", {\n callId: Schema.String,\n tool: Schema.String,\n params: Schema.Json\n}) {}\n\n/**\n * Represents tool calls that waited for the user's approval and won't run: the user moved on, like\n * the ones `ToolApproval.closeStale` expires. The model reads that they weren't approved.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalsExpired extends Schema.TaggedClass<ApprovalsExpired>()(\"ApprovalsExpired\", {\n callIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Schema for every event the library defines.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantEvent = Schema.Union([\n UserMessage,\n BlockAnswer,\n AssistantMessage,\n ModelOutput,\n ToolStarted,\n ToolResult,\n TurnStarted,\n TurnCompleted,\n TurnFailed,\n SpecialistStarted,\n SpecialistFinished,\n BlocksClosed,\n ApprovalRequested,\n ApprovalDecision,\n ApprovalsExpired,\n ChatDeleted\n])\n\n/**\n * Represents an event the library defines.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantEvent = typeof AssistantEvent.Type\n\nconst libraryTags: ReadonlySet<string> = new Set(\n AssistantEvent.members.map((member) => member.fields._tag.schema.literal)\n)\n\n/**\n * Schema for the tag of one of the application's own events.\n *\n * It accepts any tag but the library's, and it is branded, so narrowing an entry's event to one\n * of the library's events (`Predicate.isTagged(event, \"UserMessage\")`) never leaves an app event\n * in.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AppTag = Schema.String.pipe(Schema.brand(\"~effect-assistant/AppTag\"))\n\n/**\n * Represents the schema of one of the application's own events, as `Assistant.make`'s `events`\n * takes it.\n *\n * **Details**\n *\n * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) whose `_tag` is a\n * string literal other than the library's, and that needs no services. The log stores the event in\n * the schema's JSON form.\n *\n * @category models\n * @since 0.3.0\n */\nexport type AppEventSchema = Schema.Codec<{ readonly _tag: string }, { readonly _tag: string }> & {\n readonly fields: { readonly _tag: { readonly ast: SchemaAST.AST } }\n}\n\n/**\n * Schema for one of the application's own events as it is stored, with its `_tag` and its fields\n * in JSON.\n *\n * **Details**\n *\n * An assistant made with `events: [Summary]` decodes it to a `Summary` in `CurrentChat`'s\n * `entries` and in its `subscribe`.\n *\n * An event that came in through the assistant's `submit`, from outside a turn, is an input, and the\n * log marks it with a `\"~effect-assistant/input\": true` key (see {@link isAppInput}); one a turn\n * appended has none. Decoding drops the key, so the app's schemas never see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const StoredAppEvent = Schema.StructWithRest(\n Schema.Struct({ _tag: AppTag, [inputKey]: Schema.optionalKey(Schema.Literal(true)) }),\n [Schema.Record(Schema.String, Schema.Json)]\n).check(Schema.makeFilter((event) => !libraryTags.has(event._tag) || `${event._tag} is one of the library's events`))\n\n/**\n * Represents one of the application's own events, as it is stored.\n *\n * @category models\n * @since 0.1.0\n */\nexport type StoredAppEvent = typeof StoredAppEvent.Type\n\n/**\n * Returns whether an event is one of the library's, not one of the app's own.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isLibraryEvent = (event: { readonly _tag: string }): event is AssistantEvent => libraryTags.has(event._tag)\n\n/**\n * Returns whether one of the application's events, as the log stores it, is an input: it came in\n * through the assistant's `submit`, from outside a turn, so storing it woke a turn. An event a turn\n * appended (`CurrentChat`'s `append`) isn't, and neither is a decoded one, which no longer has the\n * mark.\n *\n * @category guards\n * @since 0.3.0\n */\nexport const isAppInput = (event: { readonly _tag: string }): boolean =>\n !isLibraryEvent(event) && Predicate.hasProperty(event, inputKey) && event[inputKey] === true\n\n/**\n * Represents a chat's entry with any event in it, either the library's or the application's own\n * (decoded or not).\n *\n * The folds over a chat take this type, so a client's typed entries fit too.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AnyEntry {\n readonly chatId: ChatId\n readonly seq: number\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\n/**\n * Schema for a stored event with its position in the chat.\n *\n * `seq` starts at 1 and has no gaps. `at` is the time the event was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Entry = Schema.Struct({\n chatId: ChatId,\n seq: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent, StoredAppEvent])\n})\n\n/**\n * Represents a stored event with its position in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Entry = typeof Entry.Type\n\n/**\n * Returns whether an event is one of the library's inputs, which are a user message, a block\n * answer, a finished specialist run, or a decision on a tool call waiting for approval.\n *\n * The application's events that came in through the assistant's `submit` are inputs too (see\n * {@link isAppInput}).\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isInput = (event: { readonly _tag: string }): event is Input =>\n Predicate.isTagged(event, \"UserMessage\") ||\n Predicate.isTagged(event, \"BlockAnswer\") ||\n Predicate.isTagged(event, \"SpecialistFinished\") ||\n Predicate.isTagged(event, \"ApprovalDecision\")\n\n/**\n * Returns whether an input starts a turn, which every input does except a run the assistant\n * stopped itself.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isWaking = (input: Input): boolean =>\n !(Predicate.isTagged(input, \"SpecialistFinished\") && input.status === \"stopped\")\n\n/**\n * Returns whether an input came from the chat's user, and so is owed a reply.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isUserInput = (input: Input): input is UserInput =>\n Predicate.isTagged(input, \"UserMessage\") ||\n Predicate.isTagged(input, \"BlockAnswer\") ||\n Predicate.isTagged(input, \"ApprovalDecision\")\n\n/**\n * Returns whether an event ends a turn, and so says how far the assistant has read.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isTurnEnd = (event: { readonly _tag: string }): event is TurnCompleted | TurnFailed =>\n Predicate.isTagged(event, \"TurnCompleted\") || Predicate.isTagged(event, \"TurnFailed\")\n\n/**\n * Returns the text of every message the user was shown, in order.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const assistantTexts = (entries: ReadonlyArray<AnyEntry>): ReadonlyArray<string> =>\n entries.flatMap(({ event }) =>\n isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") ? [event.text] : []\n )\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssistantEvent.js","names":["makeInputId"],"sources":["../src/AssistantEvent.ts"],"sourcesContent":["/**\n * Defines the events that can happen in a chat.\n *\n * A chat is an append-only log of these events, and the log is the source of truth. A chat client\n * receives the part of the log its user sees (its `Timeline`), and the model's history is rebuilt\n * from the log before every model call. Besides the library's events ({@link AssistantEvent}), a\n * log holds the application's own events ({@link StoredAppEvent}).\n *\n * @since 0.1.0\n */\nimport * as Response from \"effect/ai/Response\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as SchemaAST from \"effect/SchemaAST\"\nimport { ChatId } from \"./ChatId.ts\"\nimport { InputId, make as makeInputId } from \"./InputId.ts\"\nimport { inputKey } from \"./internal/input.ts\"\nimport { RunId } from \"./RunId.ts\"\n\n/**\n * Schema for a file the user attached to a message, kept by reference.\n *\n * The file itself is stored elsewhere, so the log never holds its bytes.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Attachment = Schema.Struct({\n fileId: Schema.String.check(Schema.isMaxLength(64)),\n name: Schema.String.check(Schema.isMaxLength(255)),\n mediaType: Schema.String.check(Schema.isMaxLength(127)),\n /** In bytes. */\n size: Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))\n})\n\n/**\n * The maximum number of files one message can carry.\n *\n * Each attached file is a line in every prompt after the message.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const maxAttachments = 10\n\n/**\n * Represents a file the user attached to a message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Attachment = typeof Attachment.Type\n\n/**\n * Formats a byte count for people and the model, such as `2.1 MB`.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const formatSize = (bytes: number): string =>\n bytes < 1024\n ? `${bytes} B`\n : bytes < 1024 * 1024\n ? `${(bytes / 1024).toFixed(1)} KB`\n : bytes < 1024 * 1024 * 1024\n ? `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n : `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n\n/**\n * Returns the line the model reads for an attachment.\n *\n * The line names the file and never holds its content.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const attachmentLine = (attachment: Attachment): string =>\n `[attached: ${attachment.name}, ${attachment.mediaType}, ${formatSize(attachment.size)}, id ${attachment.fileId}]`\n\n/**\n * Returns a message's text followed by a line for each attached file.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const textWithAttachments = (text: string, attachments: ReadonlyArray<Attachment> | undefined): string =>\n attachments === undefined || attachments.length === 0\n ? text\n : [text, ...attachments.map(attachmentLine)].filter((part) => part.length > 0).join(\"\\n\")\n\n/**\n * Represents text the user typed, and the files they attached.\n *\n * `id` defaults to a new one. Pass the client's own id so that a retried send stores the message\n * once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class UserMessage extends Schema.TaggedClass<UserMessage>()(\"UserMessage\", {\n id: InputId.pipe(Schema.withConstructorDefault(makeInputId())),\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(Attachment).check(Schema.isMaxLength(maxAttachments)))\n}) {}\n\n/**\n * Represents the user's answer to a block.\n *\n * It holds the id of the message the block was on, and the value in the block's answer schema\n * (in its JSON form). A chat UI shows it inside the block rather than as a message.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlockAnswer extends Schema.TaggedClass<BlockAnswer>()(\"BlockAnswer\", {\n id: InputId,\n messageId: Schema.String,\n kind: Schema.String,\n value: Schema.Unknown\n}) {}\n\n/**\n * Represents a specialist run that finished working on a brief.\n *\n * Unless the assistant stopped the run, it is an input, and a turn reads its report and decides\n * what to tell the user. Its id is fixed per run and brief, so it is stored once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistFinished extends Schema.TaggedClass<SpecialistFinished>()(\"SpecialistFinished\", {\n id: InputId,\n runId: RunId,\n specialist: Schema.String,\n /** Which brief this answers: 1 for the first, then one more per follow-up. */\n brief: Schema.Int,\n goal: Schema.String,\n status: Schema.Literals([\"answered\", \"failed\", \"stopped\", \"cut_off\"]),\n report: Schema.optional(Schema.String),\n error: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the user's decision on a tool call that waits for their approval: `\"approved\"`, so\n * the call runs, or `\"rejected\"`, with an optional short reason the model reads.\n *\n * Its id is fixed per call (see `ToolApproval.approve`), so a decision is stored once. A decision on\n * a call that isn't waiting any more (decided, or expired) isn't stored.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalDecision extends Schema.TaggedClass<ApprovalDecision>()(\"ApprovalDecision\", {\n id: InputId,\n callId: Schema.String,\n decision: Schema.Literals([\"approved\", \"rejected\"]),\n reason: Schema.optional(Schema.String.check(Schema.isMaxLength(500)))\n}) {}\n\n/**\n * Schema for every input `Assistant.submit` accepts.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Input = Schema.Union([UserMessage, BlockAnswer, SpecialistFinished, ApprovalDecision])\n\n/**\n * Represents an input to an assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Schema for the inputs from the chat's user, which are messages, block answers, and decisions on\n * tool calls that wait for their approval.\n *\n * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's\n * events come from the server.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserInput = Schema.Union([UserMessage, BlockAnswer, ApprovalDecision])\n\n/**\n * Represents an input from the chat's user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserInput = typeof UserInput.Type\n\n/**\n * Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its\n * props.\n *\n * A block that shows a tool's result (`Block.fromToolResult`) has the tool's name as its kind and\n * the result's `resultId`.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class MessageBlock extends Schema.Class<MessageBlock>(\"effect-assistant/MessageBlock\")({\n kind: Schema.String,\n props: Schema.Unknown,\n resultId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents a message the assistant sent the user with `send_user_message`, optionally with a\n * block.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class AssistantMessage extends Schema.TaggedClass<AssistantMessage>()(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n block: Schema.optional(MessageBlock),\n /** Earlier messages whose block the user answered by typing instead. Their blocks close. */\n closes: Schema.optional(Schema.Array(Schema.String))\n}) {}\n\n/**\n * Schema for a tool call the model made.\n *\n * It is `effect/ai`'s tool call part (`Response.ToolCallPart`) for any tool, with its params in\n * JSON, as the model wrote them.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolCallPart = Schema.Struct({\n ...Response.ToolCallPart(\"\", Schema.Json).fields,\n name: Schema.String\n}).annotate({ identifier: \"ToolCallPart\" })\n\n/**\n * Represents a tool call the model made.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolCallPart = typeof ToolCallPart.Type\n\n/**\n * Schema for what the log keeps of a model call.\n *\n * It uses `effect/ai`'s response parts for text, reasoning (with the provider's metadata, such as\n * a thinking block's signature), and tool calls.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ModelPart = Schema.Union([Response.TextPart, Response.ReasoningPart, ToolCallPart])\n\n/**\n * Represents a part of a model call's output.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ModelPart = typeof ModelPart.Type\n\n/**\n * Represents what one model call wrote, as response parts for text, reasoning, and tool calls.\n *\n * `Prompt.fromResponseParts` turns the parts into history.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ModelOutput extends Schema.TaggedClass<ModelOutput>()(\"ModelOutput\", {\n parts: Schema.Array(ModelPart),\n /**\n * The last entry the model had read when it wrote this. Inputs after it\n * arrived while the model was working. A turn has handled what its\n * outputs had read.\n */\n seenThroughSequence: Schema.optional(Schema.Int),\n /** The model that wrote it, as the provider's id for it or the name the context gave it. */\n model: Schema.optional(Schema.String),\n /** The provider's id for the response, when it gives one. */\n responseId: Schema.optional(Schema.String),\n /** The call's token usage, with input (including the cache's reads and writes) and output. */\n usage: Schema.optional(Response.Usage),\n /** How long the call took, in milliseconds. */\n durationMillis: Schema.optional(Schema.Finite)\n}) {}\n\n/**\n * Represents a tool call that is about to run.\n *\n * It is stored before the tool runs, so after a crash the log says which calls may have done\n * something. Those calls run again with the same call id (see `AssistantToolCall`).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolStarted extends Schema.TaggedClass<ToolStarted>()(\"ToolStarted\", {\n callId: Schema.String,\n tool: Schema.String\n}) {}\n\n/**\n * Schema for how a tool call went, either `\"success\"` or `\"failure\"`, which the model reads as an\n * error.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolOutcome = Schema.Literals([\"success\", \"failure\"])\n\n/**\n * Represents how a tool call went.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolOutcome = typeof ToolOutcome.Type\n\n/**\n * Represents the result of one tool call, as the model reads it.\n *\n * It holds the tool's success or failure in its encoded (JSON) form, and which of the two it is. A\n * failure includes its reason when it has one, which is a `ToolFailure`'s `reason` or the\n * failure's tag.\n *\n * When the model reads less than the whole success (a `ToolResult.modelView`, or the `resultId`\n * added for a tool the assistant's blocks show), `full` holds the whole success, encoded by the\n * tool's success schema.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolResult extends Schema.TaggedClass<ToolResult>()(\"ToolResult\", {\n callId: Schema.String,\n tool: Schema.String,\n outcome: ToolOutcome,\n result: Schema.Json,\n reason: Schema.optional(Schema.String),\n full: Schema.optional(Schema.Json),\n /** The id the model passes in a `tool_result` block to show this result. */\n resultId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the start of a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnStarted extends Schema.TaggedClass<TurnStarted>()(\"TurnStarted\", {\n /** The assistant that runs it. */\n assistant: Schema.String,\n /**\n * The id of the turn's trace, when a tracer records it (not Effect's own, which exports nothing).\n */\n traceId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents a turn that finished.\n *\n * `throughSequence` is the last entry the turn read. Inputs after it still need a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnCompleted extends Schema.TaggedClass<TurnCompleted>()(\"TurnCompleted\", {\n throughSequence: Schema.Int\n}) {}\n\n/**\n * Represents a turn that failed.\n *\n * `throughSequence` covers everything stored when the turn failed, so a failing input isn't retried\n * forever.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnFailed extends Schema.TaggedClass<TurnFailed>()(\"TurnFailed\", {\n throughSequence: Schema.Int,\n message: Schema.String\n}) {}\n\n/**\n * Represents a specialist run that started working, either spawned or continued by a follow-up\n * message.\n *\n * The model doesn't see it, since the call that started the run says so, and the run's report\n * arrives as a {@link SpecialistFinished}.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistStarted extends Schema.TaggedClass<SpecialistStarted>()(\"SpecialistStarted\", {\n runId: RunId,\n specialist: Schema.String,\n /** The goal it's working on. */\n goal: Schema.String\n}) {}\n\n/**\n * Represents the deletion of a chat.\n *\n * The chat stores nothing after it, the work running in the chat stopped, and its subscribers'\n * streams end here. Its log stays.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ChatDeleted extends Schema.TaggedClass<ChatDeleted>()(\"ChatDeleted\", {}) {}\n\n/**\n * Represents blocks that closed without an answer, like the ones `BlockKit.closeStale` closes\n * from a turn once the user moved on.\n *\n * The model doesn't see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlocksClosed extends Schema.TaggedClass<BlocksClosed>()(\"BlocksClosed\", {\n messageIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Represents a tool call that waits for the user's approval instead of running: its tool needs\n * approval (Effect's `needsApproval`).\n *\n * **Details**\n *\n * It holds the call's id, the tool, and its params, in JSON, as the model wrote them. The model\n * reads it as the call's result, saying it waits for the user; the user's\n * {@link ApprovalDecision} decides it, and an approved call runs then, with the same id.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalRequested extends Schema.TaggedClass<ApprovalRequested>()(\"ApprovalRequested\", {\n callId: Schema.String,\n tool: Schema.String,\n params: Schema.Json\n}) {}\n\n/**\n * Represents tool calls that waited for the user's approval and won't run: the user moved on, like\n * the ones `ToolApproval.closeStale` expires. The model reads that they weren't approved.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalsExpired extends Schema.TaggedClass<ApprovalsExpired>()(\"ApprovalsExpired\", {\n callIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Schema for every event the library defines.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantEvent = Schema.Union([\n UserMessage,\n BlockAnswer,\n AssistantMessage,\n ModelOutput,\n ToolStarted,\n ToolResult,\n TurnStarted,\n TurnCompleted,\n TurnFailed,\n SpecialistStarted,\n SpecialistFinished,\n BlocksClosed,\n ApprovalRequested,\n ApprovalDecision,\n ApprovalsExpired,\n ChatDeleted\n])\n\n/**\n * Represents an event the library defines.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantEvent = typeof AssistantEvent.Type\n\nconst libraryTags: ReadonlySet<string> = new Set(\n AssistantEvent.members.map((member) => member.fields._tag.schema.literal)\n)\n\n/**\n * Schema for the tag of one of the application's own events.\n *\n * It accepts any tag but the library's, and it is branded, so narrowing an entry's event to one\n * of the library's events (`Predicate.isTagged(event, \"UserMessage\")`) never leaves an app event\n * in.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AppTag = Schema.String.pipe(Schema.brand(\"~effect-assistant/AppTag\"))\n\n/**\n * Represents the schema of one of the application's own events, as `Assistant.make`'s `events`\n * takes it.\n *\n * **Details**\n *\n * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) whose `_tag` is a\n * string literal other than the library's, and that needs no services. The log stores the event in\n * the schema's JSON form.\n *\n * @category models\n * @since 0.3.0\n */\nexport type AppEventSchema = Schema.Codec<{ readonly _tag: string }, { readonly _tag: string }> & {\n readonly fields: { readonly _tag: { readonly ast: SchemaAST.AST } }\n}\n\n/**\n * Schema for one of the application's own events as it is stored, with its `_tag` and its fields\n * in JSON.\n *\n * **Details**\n *\n * An assistant made with `events: [Summary]` decodes it to a `Summary` in `CurrentChat`'s\n * `entries` and in its `subscribe`.\n *\n * An event that came in through the assistant's `submit`, from outside a turn, is an input, and the\n * log marks it with a `\"~effect-assistant/input\": true` key (see {@link isAppInput}); one a turn\n * appended has none. Decoding drops the key, so the app's schemas never see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const StoredAppEvent = Schema.StructWithRest(\n Schema.Struct({ _tag: AppTag, [inputKey]: Schema.optionalKey(Schema.Literal(true)) }),\n [Schema.Record(Schema.String, Schema.Json)]\n).check(Schema.makeFilter((event) => !libraryTags.has(event._tag) || `${event._tag} is one of the library's events`))\n\n/**\n * Represents one of the application's own events, as it is stored.\n *\n * @category models\n * @since 0.1.0\n */\nexport type StoredAppEvent = typeof StoredAppEvent.Type\n\n/**\n * Returns whether an event is one of the library's, not one of the app's own.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isLibraryEvent = (event: { readonly _tag: string }): event is AssistantEvent => libraryTags.has(event._tag)\n\n/**\n * Returns whether one of the application's events, as the log stores it, is an input: it came in\n * through the assistant's `submit`, from outside a turn, so storing it woke a turn. An event a turn\n * appended (`CurrentChat`'s `append`) isn't, and neither is a decoded one, which no longer has the\n * mark.\n *\n * @category guards\n * @since 0.3.0\n */\nexport const isAppInput = (event: { readonly _tag: string }): boolean =>\n !isLibraryEvent(event) && Predicate.hasProperty(event, inputKey) && event[inputKey] === true\n\n/**\n * Represents a chat's entry with any event in it, either the library's or the application's own\n * (decoded or not).\n *\n * The folds over a chat take this type, so a client's typed entries fit too.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AnyEntry {\n readonly chatId: ChatId\n readonly sequence: number\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\n/**\n * Schema for a stored event with its position in the chat.\n *\n * `sequence` starts at 1 and has no gaps. `at` is the time the event was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Entry = Schema.Struct({\n chatId: ChatId,\n sequence: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent, StoredAppEvent])\n})\n\n/**\n * Represents a stored event with its position in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Entry = typeof Entry.Type\n\n/**\n * Returns whether an event is one of the library's inputs, which are a user message, a block\n * answer, a finished specialist run, or a decision on a tool call waiting for approval.\n *\n * The application's events that came in through the assistant's `submit` are inputs too (see\n * {@link isAppInput}).\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isInput = (event: { readonly _tag: string }): event is Input =>\n Predicate.isTagged(event, \"UserMessage\") ||\n Predicate.isTagged(event, \"BlockAnswer\") ||\n Predicate.isTagged(event, \"SpecialistFinished\") ||\n Predicate.isTagged(event, \"ApprovalDecision\")\n\n/**\n * Returns whether an input starts a turn, which every input does except a run the assistant\n * stopped itself.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isWaking = (input: Input): boolean =>\n !(Predicate.isTagged(input, \"SpecialistFinished\") && input.status === \"stopped\")\n\n/**\n * Returns whether an input came from the chat's user, and so is owed a reply.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isUserInput = (input: Input): input is UserInput =>\n Predicate.isTagged(input, \"UserMessage\") ||\n Predicate.isTagged(input, \"BlockAnswer\") ||\n Predicate.isTagged(input, \"ApprovalDecision\")\n\n/**\n * Returns whether an event ends a turn, and so says how far the assistant has read.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isTurnEnd = (event: { readonly _tag: string }): event is TurnCompleted | TurnFailed =>\n Predicate.isTagged(event, \"TurnCompleted\") || Predicate.isTagged(event, \"TurnFailed\")\n\n/**\n * Returns the text of every message the user was shown, in order.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const assistantTexts = (entries: ReadonlyArray<AnyEntry>): ReadonlyArray<string> =>\n entries.flatMap(({ event }) =>\n isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") ? [event.text] : []\n )\n"],"mappings":"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"}
|
package/dist/AssistantLoop.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type Event = Event$1;
|
|
|
24
24
|
/**
|
|
25
25
|
* Represents a chat's history as the model reads it.
|
|
26
26
|
*
|
|
27
|
-
* It holds the messages, and the last `
|
|
27
|
+
* It holds the messages, and the last `sequence` of the chat they cover.
|
|
28
28
|
*
|
|
29
29
|
* @category models
|
|
30
30
|
* @since 0.1.0
|
|
@@ -100,7 +100,7 @@ export interface StepOptions<T extends Record<string, Tool.Any> = NoTools> {
|
|
|
100
100
|
readonly toolkit?: Toolkit.Toolkit<T> | undefined;
|
|
101
101
|
/**
|
|
102
102
|
* The current history, read at the start of the step. It is either messages, or a chat's
|
|
103
|
-
* history (`turn.history()`), whose `
|
|
103
|
+
* history (`turn.history()`), whose `throughSequence` the step's `ModelOutput` records as how far
|
|
104
104
|
* the model had read.
|
|
105
105
|
*/
|
|
106
106
|
readonly history: Effect.Effect<ReadonlyArray<Prompt.Message> | ChatHistory>;
|
|
@@ -1 +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.
|
|
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.run` 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 `sequence` 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 `throughSequence` 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 * In a turn's loop, a call to a tool with Effect's `needsApproval` doesn't run: the step emits an\n * `ApprovalRequested` for it, and ends the turn, which waits for the user (see `ToolApproval`). A\n * call the user approved runs at the start of the next step, before the model is called. A\n * specialist's run can't wait for the user: such a call fails, and the run reads why.\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;;;;;;;;;;;;;;;;;;;AAoBH,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"}
|
package/dist/Block.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as Tool from "effect/ai/Tool";
|
|
2
3
|
declare namespace Block_d_exports {
|
|
3
|
-
export { Any, Block, Codec, make };
|
|
4
|
+
export { Any, Block, Codec, NoProps, ToolResultBlock, fromToolResult, isToolResult, make };
|
|
4
5
|
}
|
|
5
6
|
/**
|
|
6
7
|
* Schema that a block's props or answer can use.
|
|
@@ -64,6 +65,67 @@ export declare const make: <const Kind extends string, Props extends Codec, Answ
|
|
|
64
65
|
readonly props: Props;
|
|
65
66
|
readonly answer?: Answer | undefined;
|
|
66
67
|
}) => Block<Kind, Props, Answer>;
|
|
68
|
+
/**
|
|
69
|
+
* Schema for the props of a block that shows a tool's result: none, since the result is the block.
|
|
70
|
+
*
|
|
71
|
+
* @category schemas
|
|
72
|
+
* @since 0.5.0
|
|
73
|
+
*/
|
|
74
|
+
export declare const NoProps: Schema.Struct<{}>;
|
|
75
|
+
/**
|
|
76
|
+
* Represents a block that shows a tool's results, made with {@link fromToolResult}. Its kind is the
|
|
77
|
+
* tool's name.
|
|
78
|
+
*
|
|
79
|
+
* @category models
|
|
80
|
+
* @since 0.5.0
|
|
81
|
+
*/
|
|
82
|
+
export interface ToolResultBlock<T extends Tool.Any = Tool.Any> extends Block<Tool.Name<T>, typeof NoProps, Schema.Never> {
|
|
83
|
+
readonly tool: T;
|
|
84
|
+
/** The tool's success schema, which the client decodes the result with. */
|
|
85
|
+
readonly result: Codec;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a block that shows a tool's results.
|
|
89
|
+
*
|
|
90
|
+
* **Details**
|
|
91
|
+
*
|
|
92
|
+
* Put it in the assistant's `BlockKit`. The tool's results then carry a `resultId` in what the model
|
|
93
|
+
* reads, and the model shows one with a `tool_result` block that names it: it never sees the
|
|
94
|
+
* block's kind or how the client draws it. Sending the block fails back to the model when no
|
|
95
|
+
* successful result of a shown tool has that id.
|
|
96
|
+
*
|
|
97
|
+
* On the client, `BlockKit.toClient` gives the block's renderer the result, decoded by the tool's
|
|
98
|
+
* success schema. The tool's definition is shared with the client for that, so it can't need
|
|
99
|
+
* server-only services: add those on the server with `Tool.addDependency`.
|
|
100
|
+
*
|
|
101
|
+
* **Example** (Showing search results as cards)
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { Block, BlockKit } from "effect-assistant"
|
|
105
|
+
* import { SearchAssets } from "./tools.ts"
|
|
106
|
+
*
|
|
107
|
+
* export const Blocks = BlockKit.make([PickOne, Block.fromToolResult(SearchAssets)])
|
|
108
|
+
*
|
|
109
|
+
* // Client
|
|
110
|
+
* const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {
|
|
111
|
+
* pick_one: ({ props, respond }) => <Options options={props.options} onPick={respond} />,
|
|
112
|
+
* search_assets: ({ result }) => <AssetCards assets={result.items} />
|
|
113
|
+
* })
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @category constructors
|
|
117
|
+
* @since 0.5.0
|
|
118
|
+
*/
|
|
119
|
+
export declare const fromToolResult: <T extends Tool.Any>(tool: T & {
|
|
120
|
+
readonly successSchema: Codec;
|
|
121
|
+
}) => ToolResultBlock<T>;
|
|
122
|
+
/**
|
|
123
|
+
* Returns whether a block shows a tool's results.
|
|
124
|
+
*
|
|
125
|
+
* @category guards
|
|
126
|
+
* @since 0.5.0
|
|
127
|
+
*/
|
|
128
|
+
export declare const isToolResult: (block: Any) => block is ToolResultBlock;
|
|
67
129
|
//#endregion
|
|
68
130
|
export { Block_d_exports };
|
|
69
131
|
//# sourceMappingURL=Block.d.ts.map
|
package/dist/Block.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Block.d.ts","names":[],"sources":["../src/Block.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Block.d.ts","names":[],"sources":["../src/Block.ts"],"mappings":";;;;;;;;;;;;;YAwBY,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;;;;;;;qBAaT,SAAO,OAAA;;;;;;;;iBASH,gBAAgB,UAAU,KAAK,MAAM,KAAK,aAAa,MACtE,KAAK,KAAK,WACH,SACP,OAAO;WAEE,MAAM;;WAEN,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmCN,iBAAkB,UAAU,KAAK,KAAG,MACzC;WAAe,eAAe;MACnC,gBAAgB;;;;;;;qBAeN,eAAY,OAAW,QAAM,SAAS"}
|
package/dist/Block.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as Predicate from "effect/Predicate";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
2
4
|
//#region src/Block.ts
|
|
3
|
-
var Block_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
var Block_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
NoProps: () => NoProps,
|
|
7
|
+
fromToolResult: () => fromToolResult,
|
|
8
|
+
isToolResult: () => isToolResult,
|
|
9
|
+
make: () => make
|
|
10
|
+
});
|
|
4
11
|
/**
|
|
5
12
|
* Creates a block.
|
|
6
13
|
*
|
|
@@ -33,7 +40,61 @@ const make = (kind, options) => ({
|
|
|
33
40
|
props: options.props,
|
|
34
41
|
answer: options.answer
|
|
35
42
|
});
|
|
43
|
+
/**
|
|
44
|
+
* Schema for the props of a block that shows a tool's result: none, since the result is the block.
|
|
45
|
+
*
|
|
46
|
+
* @category schemas
|
|
47
|
+
* @since 0.5.0
|
|
48
|
+
*/
|
|
49
|
+
const NoProps = Schema.Struct({});
|
|
50
|
+
/**
|
|
51
|
+
* Creates a block that shows a tool's results.
|
|
52
|
+
*
|
|
53
|
+
* **Details**
|
|
54
|
+
*
|
|
55
|
+
* Put it in the assistant's `BlockKit`. The tool's results then carry a `resultId` in what the model
|
|
56
|
+
* reads, and the model shows one with a `tool_result` block that names it: it never sees the
|
|
57
|
+
* block's kind or how the client draws it. Sending the block fails back to the model when no
|
|
58
|
+
* successful result of a shown tool has that id.
|
|
59
|
+
*
|
|
60
|
+
* On the client, `BlockKit.toClient` gives the block's renderer the result, decoded by the tool's
|
|
61
|
+
* success schema. The tool's definition is shared with the client for that, so it can't need
|
|
62
|
+
* server-only services: add those on the server with `Tool.addDependency`.
|
|
63
|
+
*
|
|
64
|
+
* **Example** (Showing search results as cards)
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { Block, BlockKit } from "effect-assistant"
|
|
68
|
+
* import { SearchAssets } from "./tools.ts"
|
|
69
|
+
*
|
|
70
|
+
* export const Blocks = BlockKit.make([PickOne, Block.fromToolResult(SearchAssets)])
|
|
71
|
+
*
|
|
72
|
+
* // Client
|
|
73
|
+
* const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {
|
|
74
|
+
* pick_one: ({ props, respond }) => <Options options={props.options} onPick={respond} />,
|
|
75
|
+
* search_assets: ({ result }) => <AssetCards assets={result.items} />
|
|
76
|
+
* })
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @category constructors
|
|
80
|
+
* @since 0.5.0
|
|
81
|
+
*/
|
|
82
|
+
const fromToolResult = (tool) => ({
|
|
83
|
+
kind: tool.name,
|
|
84
|
+
description: `Shows a ${tool.name} result.`,
|
|
85
|
+
props: NoProps,
|
|
86
|
+
answer: void 0,
|
|
87
|
+
tool,
|
|
88
|
+
result: tool.successSchema
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Returns whether a block shows a tool's results.
|
|
92
|
+
*
|
|
93
|
+
* @category guards
|
|
94
|
+
* @since 0.5.0
|
|
95
|
+
*/
|
|
96
|
+
const isToolResult = (block) => Predicate.hasProperty(block, "tool");
|
|
36
97
|
//#endregion
|
|
37
|
-
export { Block_exports, make };
|
|
98
|
+
export { Block_exports, NoProps, fromToolResult, isToolResult, make };
|
|
38
99
|
|
|
39
100
|
//# sourceMappingURL=Block.js.map
|
package/dist/Block.js.map
CHANGED
|
@@ -1 +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":"
|
|
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 * A block made with {@link fromToolResult} shows a tool's result instead: the assistant picks the\n * result, and the client draws it from what the tool returned.\n *\n * @since 0.1.0\n */\nimport type * as Tool from \"effect/ai/Tool\"\nimport * as Predicate from \"effect/Predicate\"\nimport * 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\n/**\n * Schema for the props of a block that shows a tool's result: none, since the result is the block.\n *\n * @category schemas\n * @since 0.5.0\n */\nexport const NoProps = Schema.Struct({})\n\n/**\n * Represents a block that shows a tool's results, made with {@link fromToolResult}. Its kind is the\n * tool's name.\n *\n * @category models\n * @since 0.5.0\n */\nexport interface ToolResultBlock<T extends Tool.Any = Tool.Any> extends Block<\n Tool.Name<T>,\n typeof NoProps,\n Schema.Never\n> {\n readonly tool: T\n /** The tool's success schema, which the client decodes the result with. */\n readonly result: Codec\n}\n\n/**\n * Creates a block that shows a tool's results.\n *\n * **Details**\n *\n * Put it in the assistant's `BlockKit`. The tool's results then carry a `resultId` in what the model\n * reads, and the model shows one with a `tool_result` block that names it: it never sees the\n * block's kind or how the client draws it. Sending the block fails back to the model when no\n * successful result of a shown tool has that id.\n *\n * On the client, `BlockKit.toClient` gives the block's renderer the result, decoded by the tool's\n * success schema. The tool's definition is shared with the client for that, so it can't need\n * server-only services: add those on the server with `Tool.addDependency`.\n *\n * **Example** (Showing search results as cards)\n *\n * ```ts\n * import { Block, BlockKit } from \"effect-assistant\"\n * import { SearchAssets } from \"./tools.ts\"\n *\n * export const Blocks = BlockKit.make([PickOne, Block.fromToolResult(SearchAssets)])\n *\n * // Client\n * const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {\n * pick_one: ({ props, respond }) => <Options options={props.options} onPick={respond} />,\n * search_assets: ({ result }) => <AssetCards assets={result.items} />\n * })\n * ```\n *\n * @category constructors\n * @since 0.5.0\n */\nexport const fromToolResult = <T extends Tool.Any>(\n tool: T & { readonly successSchema: Codec }\n): ToolResultBlock<T> => ({\n kind: tool.name,\n description: `Shows a ${tool.name} result.`,\n props: NoProps,\n answer: undefined,\n tool,\n result: tool.successSchema\n})\n\n/**\n * Returns whether a block shows a tool's results.\n *\n * @category guards\n * @since 0.5.0\n */\nexport const isToolResult = (block: Any): block is ToolResultBlock => Predicate.hasProperty(block, \"tool\")\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,MAAa,QACX,MACA,aAKgC;CAChC;CACA,aAAa,QAAQ;CACrB,OAAO,QAAQ;CACf,QAAQ,QAAQ;AAClB;;;;;;;AAQA,MAAa,UAAU,OAAO,OAAO,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDvC,MAAa,kBACX,UACwB;CACxB,MAAM,KAAK;CACX,aAAa,WAAW,KAAK,KAAK;CAClC,OAAO;CACP,QAAQ,KAAA;CACR;CACA,QAAQ,KAAK;AACf;;;;;;;AAQA,MAAa,gBAAgB,UAAyC,UAAU,YAAY,OAAO,MAAM"}
|
package/dist/BlockKit.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { AssistantMessage, Timeline_d_exports } from "./Timeline.js";
|
|
|
4
4
|
import { CurrentChat } from "./CurrentChat.js";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as Tool from "effect/ai/Tool";
|
|
7
8
|
declare namespace BlockKit_d_exports {
|
|
8
|
-
export { BlockKit, RenderArgs, RenderOptions, Renderers, State, apply, closeStale, fromEntries, make, staleBlocks, toClient };
|
|
9
|
+
export { BlockArgs, BlockKit, RenderArgs, RenderOptions, Renderers, State, apply, closeStale, fromEntries, make, staleBlocks, toClient, toolNames };
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Represents a catalog of blocks, with a lookup by kind.
|
|
@@ -24,6 +25,13 @@ export interface BlockKit<Blocks extends Any = Any> {
|
|
|
24
25
|
* @since 0.1.0
|
|
25
26
|
*/
|
|
26
27
|
export declare const make: <const Blocks extends ReadonlyArray<Any>>(blocks: Blocks) => BlockKit<Blocks[number]>;
|
|
28
|
+
/**
|
|
29
|
+
* Returns the names of the tools whose results the kit's blocks show (`Block.fromToolResult`).
|
|
30
|
+
*
|
|
31
|
+
* @category utils
|
|
32
|
+
* @since 0.5.0
|
|
33
|
+
*/
|
|
34
|
+
export declare const toolNames: (kit: BlockKit | undefined) => ReadonlySet<string>;
|
|
27
35
|
type KindOf<B> = B extends {
|
|
28
36
|
readonly kind: infer K extends string;
|
|
29
37
|
} ? K : never;
|
|
@@ -36,6 +44,12 @@ type PropsOf<B> = B extends {
|
|
|
36
44
|
type AnswerOf<B> = B extends {
|
|
37
45
|
readonly answer: infer A;
|
|
38
46
|
} ? (A extends Schema.Top ? A["Type"] : never) : never;
|
|
47
|
+
type ResultArgs<B> = B extends {
|
|
48
|
+
readonly tool: infer T;
|
|
49
|
+
} ? {
|
|
50
|
+
/** The tool's result the block shows, decoded by its success schema. */
|
|
51
|
+
readonly result: Tool.Success<T>;
|
|
52
|
+
} : unknown;
|
|
39
53
|
/**
|
|
40
54
|
* Represents the state of a message's block.
|
|
41
55
|
*
|
|
@@ -95,12 +109,20 @@ export declare const closeStale: (options: {
|
|
|
95
109
|
readonly messages: number;
|
|
96
110
|
}) => Effect.Effect<void, never, CurrentChat>;
|
|
97
111
|
/**
|
|
98
|
-
* Represents what a renderer receives for one block.
|
|
112
|
+
* Represents what a renderer receives for one block. A block that shows a tool's result also gets
|
|
113
|
+
* the `result`.
|
|
99
114
|
*
|
|
100
115
|
* @category models
|
|
101
116
|
* @since 0.1.0
|
|
102
117
|
*/
|
|
103
|
-
export
|
|
118
|
+
export type RenderArgs<B> = BlockArgs<B> & ResultArgs<B>;
|
|
119
|
+
/**
|
|
120
|
+
* Represents what every block's renderer receives.
|
|
121
|
+
*
|
|
122
|
+
* @category models
|
|
123
|
+
* @since 0.5.0
|
|
124
|
+
*/
|
|
125
|
+
export interface BlockArgs<B> {
|
|
104
126
|
/**
|
|
105
127
|
* The id of the message the block is on. A block whose button calls your API can key the work
|
|
106
128
|
* on it, with the chat's id, so the work runs once per block.
|
package/dist/BlockKit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockKit.d.ts","names":[],"sources":["../src/BlockKit.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"BlockKit.d.ts","names":[],"sources":["../src/BlockKit.ts"],"mappings":";;;;;;;;;;;;;;;;iBA2BiB,SAAS,eAAe,MAAY;WAC1C,QAAQ,cAAc;WACtB,QAAQ,oBAAoB;;;;;;;;qBAS1B,aAAc,eAAe,cAAc,MAAU,QAAU,WAAS,SAAS;;;;;;;qBAWjF,YAAS,KAAS,yBAAuB;KAGjD,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;KAE/E,WAAW,KAAK;WAAqB,YAAY;;;WAGvC,QAAQ,KAAK,QAAQ;;;;;;;;iBAUnB;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;;;;;;;;YASU,WAAW,KAAK,UAAU,KAAK,WAAW;;;;;;;iBAQrC,UAAU;;;;;WAKhB;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
CHANGED
|
@@ -2,28 +2,20 @@ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
|
2
2
|
import { fromKey } from "./InputId.js";
|
|
3
3
|
import { BlockAnswer, BlocksClosed, isLibraryEvent } from "./AssistantEvent.js";
|
|
4
4
|
import { CurrentChat } from "./CurrentChat.js";
|
|
5
|
+
import { isToolResult } from "./Block.js";
|
|
5
6
|
import * as Effect from "effect/Effect";
|
|
6
7
|
import * as Predicate from "effect/Predicate";
|
|
7
8
|
import * as Schema from "effect/Schema";
|
|
8
9
|
import * as Option from "effect/Option";
|
|
9
10
|
//#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
11
|
var BlockKit_exports = /* @__PURE__ */ __exportAll({
|
|
21
12
|
apply: () => apply,
|
|
22
13
|
closeStale: () => closeStale,
|
|
23
14
|
fromEntries: () => fromEntries,
|
|
24
15
|
make: () => make,
|
|
25
16
|
staleBlocks: () => staleBlocks,
|
|
26
|
-
toClient: () => toClient
|
|
17
|
+
toClient: () => toClient,
|
|
18
|
+
toolNames: () => toolNames
|
|
27
19
|
});
|
|
28
20
|
/**
|
|
29
21
|
* Creates a kit from blocks.
|
|
@@ -36,6 +28,13 @@ const make = (blocks) => ({
|
|
|
36
28
|
byKind: new Map(blocks.map((block) => [block.kind, block]))
|
|
37
29
|
});
|
|
38
30
|
/**
|
|
31
|
+
* Returns the names of the tools whose results the kit's blocks show (`Block.fromToolResult`).
|
|
32
|
+
*
|
|
33
|
+
* @category utils
|
|
34
|
+
* @since 0.5.0
|
|
35
|
+
*/
|
|
36
|
+
const toolNames = (kit) => new Set((kit?.blocks ?? []).flatMap((block) => isToolResult(block) ? [block.tool.name] : []));
|
|
37
|
+
/**
|
|
39
38
|
* Returns the block states after one more event.
|
|
40
39
|
*
|
|
41
40
|
* @category utils
|
|
@@ -124,6 +123,8 @@ function toClient(kit, renderers) {
|
|
|
124
123
|
if (block === void 0 || render === void 0) return void 0;
|
|
125
124
|
const props = Schema.decodeUnknownOption(block.props)(message.block.props);
|
|
126
125
|
if (Option.isNone(props)) return void 0;
|
|
126
|
+
const result = isToolResult(block) ? Schema.decodeUnknownOption(Schema.toCodecJson(block.result))(message.block.result) : Option.some(void 0);
|
|
127
|
+
if (Option.isNone(result)) return void 0;
|
|
127
128
|
const given = message.block.answer;
|
|
128
129
|
const answerCodec = block.answer === void 0 ? void 0 : Schema.toCodecJson(block.answer);
|
|
129
130
|
const answer = given === void 0 || answerCodec === void 0 ? Option.none() : Schema.decodeUnknownOption(answerCodec)(given);
|
|
@@ -131,6 +132,7 @@ function toClient(kit, renderers) {
|
|
|
131
132
|
messageId: message.id,
|
|
132
133
|
text: message.text,
|
|
133
134
|
props: props.value,
|
|
135
|
+
result: result.value,
|
|
134
136
|
open: message.block.status === "open",
|
|
135
137
|
answer: Option.getOrUndefined(answer),
|
|
136
138
|
respond: (value) => options.respond(BlockAnswer.make({
|
|
@@ -143,6 +145,6 @@ function toClient(kit, renderers) {
|
|
|
143
145
|
};
|
|
144
146
|
}
|
|
145
147
|
//#endregion
|
|
146
|
-
export { BlockKit_exports, apply, closeStale, fromEntries, make, staleBlocks, toClient };
|
|
148
|
+
export { BlockKit_exports, apply, closeStale, fromEntries, make, staleBlocks, toClient, toolNames };
|
|
147
149
|
|
|
148
150
|
//# sourceMappingURL=BlockKit.js.map
|