effect-assistant 0.3.0 → 0.5.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 +5 -11
- package/dist/Assistant.d.ts.map +1 -1
- package/dist/Assistant.js.map +1 -1
- package/dist/AssistantEvent.d.ts +66 -7
- package/dist/AssistantEvent.d.ts.map +1 -1
- package/dist/AssistantEvent.js +74 -9
- package/dist/AssistantEvent.js.map +1 -1
- package/dist/AssistantLoop.d.ts +4 -2
- package/dist/AssistantLoop.d.ts.map +1 -1
- package/dist/AssistantLoop.js +4 -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/Compaction.d.ts +1 -1
- package/dist/Compaction.d.ts.map +1 -1
- package/dist/Compaction.js +11 -4
- package/dist/Compaction.js.map +1 -1
- package/dist/Skill.d.ts +85 -43
- package/dist/Skill.d.ts.map +1 -1
- package/dist/Skill.js +93 -44
- package/dist/Skill.js.map +1 -1
- package/dist/Specialist.d.ts +6 -9
- package/dist/Specialist.d.ts.map +1 -1
- package/dist/Specialist.js.map +1 -1
- package/dist/Timeline.d.ts +44 -6
- package/dist/Timeline.d.ts.map +1 -1
- package/dist/Timeline.js +87 -16
- package/dist/Timeline.js.map +1 -1
- package/dist/ToolApproval.d.ts +146 -0
- package/dist/ToolApproval.d.ts.map +1 -0
- package/dist/ToolApproval.js +201 -0
- package/dist/ToolApproval.js.map +1 -0
- package/dist/ToolDiscovery.d.ts +6 -4
- package/dist/ToolDiscovery.d.ts.map +1 -1
- package/dist/ToolDiscovery.js +5 -4
- 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 +4 -2
- package/dist/index.js +6 -4
- package/dist/internal/chat.js +4 -1
- package/dist/internal/chat.js.map +1 -1
- package/dist/internal/history.js +28 -3
- package/dist/internal/history.js.map +1 -1
- package/dist/internal/loop.d.ts +10 -5
- package/dist/internal/loop.d.ts.map +1 -1
- package/dist/internal/loop.js +60 -21
- package/dist/internal/loop.js.map +1 -1
- package/dist/internal/specialists.js +9 -16
- package/dist/internal/specialists.js.map +1 -1
- package/dist/internal/toolkit.d.ts +11 -7
- package/dist/internal/toolkit.d.ts.map +1 -1
- package/dist/internal/toolkit.js +74 -21
- 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 -16
- package/dist/internal/turns.js.map +1 -1
- package/package.json +1 -1
- package/src/Assistant.ts +7 -13
- package/src/AssistantEvent.ts +73 -8
- package/src/AssistantLoop.ts +4 -2
- package/src/Block.ts +82 -1
- package/src/BlockKit.ts +37 -4
- package/src/Compaction.ts +19 -2
- package/src/Skill.ts +134 -57
- package/src/Specialist.ts +8 -11
- package/src/Timeline.ts +120 -21
- package/src/ToolApproval.ts +249 -0
- package/src/ToolDiscovery.ts +8 -3
- package/src/ToolResult.ts +80 -0
- package/src/index.ts +10 -0
- package/src/internal/chat.ts +11 -1
- package/src/internal/history.ts +58 -3
- package/src/internal/loop.ts +138 -35
- package/src/internal/specialists.ts +7 -13
- package/src/internal/toolkit.ts +102 -35
- package/src/internal/tools.ts +81 -5
- package/src/internal/turns.ts +31 -10
- package/dist/internal/skills.js +0 -33
- package/dist/internal/skills.js.map +0 -1
- package/src/internal/skills.ts +0 -48
|
@@ -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 * 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])\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 and block answers.\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])\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 * 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 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, or a finished specialist run.\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\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\") || Predicate.isTagged(input, \"BlockAnswer\")\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 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 * 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 * `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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"}
|
package/dist/AssistantLoop.d.ts
CHANGED
|
@@ -130,8 +130,10 @@ export interface Options<T extends Record<string, Tool.Any> = NoTools> extends S
|
|
|
130
130
|
* the step's result and decides what comes next, with `AssistantLoop.next(state)` or
|
|
131
131
|
* `AssistantLoop.stop()`.
|
|
132
132
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
133
|
+
* In a turn's loop, a call to a tool with Effect's `needsApproval` doesn't run: the step emits an
|
|
134
|
+
* `ApprovalRequested` for it, and ends the turn, which waits for the user (see `ToolApproval`). A
|
|
135
|
+
* call the user approved runs at the start of the next step, before the model is called. A
|
|
136
|
+
* specialist's run can't wait for the user: such a call fails, and the run reads why.
|
|
135
137
|
*
|
|
136
138
|
* @category constructors
|
|
137
139
|
* @since 0.1.0
|
|
@@ -1 +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
|
|
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;;;;;;;;;;;;;;;;;;;;qBA4BE,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"}
|
package/dist/AssistantLoop.js
CHANGED
|
@@ -61,8 +61,10 @@ const toolsOf = (toolkit) => Effect.map(TurnTools, (turn) => {
|
|
|
61
61
|
* the step's result and decides what comes next, with `AssistantLoop.next(state)` or
|
|
62
62
|
* `AssistantLoop.stop()`.
|
|
63
63
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* In a turn's loop, a call to a tool with Effect's `needsApproval` doesn't run: the step emits an
|
|
65
|
+
* `ApprovalRequested` for it, and ends the turn, which waits for the user (see `ToolApproval`). A
|
|
66
|
+
* call the user approved runs at the start of the next step, before the model is called. A
|
|
67
|
+
* specialist's run can't wait for the user: such a call fails, and the run reads why.
|
|
66
68
|
*
|
|
67
69
|
* @category constructors
|
|
68
70
|
* @since 0.1.0
|
|
@@ -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.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 *
|
|
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 * 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
|
package/dist/BlockKit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockKit.js","names":["AssistantEvent.isLibraryEvent","InputId.fromKey"],"sources":["../src/BlockKit.ts"],"sourcesContent":["/**\n * Groups the blocks an assistant may put in its messages, and renders them on the client.\n *\n * A kit is created with {@link make} and passed to `Assistant.make`. {@link fromEntries} computes\n * the state of every message's block from a chat's log, {@link closeStale} closes the blocks the\n * user moved on from, and {@link toClient} creates a client-side renderer with one function per\n * block kind.\n *\n * @since 0.1.0\n */\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport type * as Block from \"./Block.ts\"\nimport type * as Timeline from \"./Timeline.ts\"\nimport { CurrentChat } from \"./CurrentChat.ts\"\nimport * as InputId from \"./InputId.ts\"\n\n/**\n * Represents a catalog of blocks, with a lookup by kind.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface BlockKit<Blocks extends Block.Any = Block.Any> {\n readonly blocks: ReadonlyArray<Blocks>\n readonly byKind: ReadonlyMap<string, Blocks>\n}\n\n/**\n * Creates a kit from blocks.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <const Blocks extends ReadonlyArray<Block.Any>>(blocks: Blocks): BlockKit<Blocks[number]> => ({\n blocks,\n byKind: new Map(blocks.map((block) => [block.kind, block]))\n})\n\ntype KindOf<B> = B extends { readonly kind: infer K extends string } ? K : never\n\ntype BlockOf<Kit extends BlockKit, K> = Extract<Kit[\"blocks\"][number], { readonly kind: K }>\n\ntype PropsOf<B> = B extends { readonly props: Schema.Top } ? B[\"props\"][\"Type\"] : never\n\ntype AnswerOf<B> = B extends { readonly answer: infer A } ? (A extends Schema.Top ? A[\"Type\"] : never) : never\n\n/**\n * Represents the state of a message's block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface State {\n readonly open: boolean\n /** The user's answer, if they used the block. */\n readonly answer: AssistantEvent.BlockAnswer | undefined\n}\n\n/**\n * Returns the block states after one more event.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const apply = (\n states: ReadonlyMap<string, State>,\n event: { readonly _tag: string }\n): ReadonlyMap<string, State> => {\n if (!AssistantEvent.isLibraryEvent(event)) return states\n\n const close = (ids: ReadonlyArray<string>) => {\n const open = ids.filter((id) => states.get(id)?.open === true)\n\n if (open.length === 0) return states\n\n const next = new Map(states)\n\n for (const id of open) next.set(id, { open: false, answer: undefined })\n\n return next\n }\n\n if (Predicate.isTagged(event, \"BlocksClosed\")) return close(event.messageIds)\n\n if (Predicate.isTagged(event, \"AssistantMessage\")) {\n const closed = close(event.closes ?? [])\n\n return event.block === undefined ? closed : new Map(closed).set(event.id, { open: true, answer: undefined })\n }\n\n if (!Predicate.isTagged(event, \"BlockAnswer\") || states.get(event.messageId)?.open !== true) return states\n\n return new Map(states).set(event.messageId, { open: false, answer: event })\n}\n\n/**\n * Returns the state of every message's block, by message id.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const fromEntries = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyMap<string, State> => {\n let states: ReadonlyMap<string, State> = new Map()\n\n for (const entry of entries) states = apply(states, entry.event)\n\n return states\n}\n\n/**\n * Returns the ids of the messages whose blocks are still open after the user typed the given\n * number of messages without using them.\n *\n * **Details**\n *\n * These are the blocks {@link closeStale} closes.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const staleBlocks = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n options: { readonly messages: number }\n): ReadonlyArray<string> => {\n const open = [...fromEntries(entries)].flatMap(([id, state]) => (state.open ? [id] : []))\n\n return open.filter((id) => {\n const shown = entries.findIndex(\n ({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") && event.id === id\n )\n\n if (shown === -1) return false\n\n const typed = entries.slice(shown + 1).filter(({ event }) => Predicate.isTagged(event, \"UserMessage\")).length\n\n return typed >= options.messages\n })\n}\n\n/**\n * Closes the blocks the user moved on from, which are still open after the given number of typed\n * messages.\n *\n * **Details**\n *\n * Run it from a turn, before or after its loop. It records a `BlocksClosed` event, so every\n * client folds the blocks closed.\n *\n * @see {@link staleBlocks} for the blocks it closes\n *\n * @category utils\n * @since 0.1.0\n */\nexport const closeStale = Effect.fn(function* (options: { readonly messages: number }) {\n const chat = yield* CurrentChat\n const stale = staleBlocks(yield* chat.entries, options)\n\n if (stale.length > 0) yield* chat.append(AssistantEvent.BlocksClosed.make({ messageIds: stale }))\n})\n\n/**\n * Represents what a renderer receives for one block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface RenderArgs<B> {\n /**\n * The id of the message the block is on. A block whose button calls your API can key the work\n * on it, with the chat's id, so the work runs once per block.\n */\n readonly messageId: string\n readonly text: string\n readonly props: PropsOf<B>\n /**\n * Whether the block is open. It is `false` once the block is answered, with the block or in\n * words; show it read-only then.\n */\n readonly open: boolean\n /** The user's answer, if they used the block. */\n readonly answer: AnswerOf<B> | undefined\n /** Sends an answer. Only call it while `open`. */\n readonly respond: (answer: AnswerOf<B>) => void\n}\n\n/**\n * Represents a renderer for every kind in a kit.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Renderers<Kit extends BlockKit, Out> = {\n readonly [K in KindOf<Kit[\"blocks\"][number]>]: (args: RenderArgs<BlockOf<Kit, K>>) => Out\n}\n\n/**\n * Represents the options for rendering one message's block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface RenderOptions {\n /** Submits an answer, for example over RPC. */\n readonly respond: (answer: AssistantEvent.BlockAnswer) => void\n}\n\n/**\n * Creates a client-side renderer for the blocks on the assistant's messages in a chat's timeline\n * (`Timeline.AssistantMessage`).\n *\n * **Details**\n *\n * The renderers are type-checked, so every kind in the kit needs one, and each receives typed\n * `props` and `answer`. A renderer's `respond` builds a `BlockAnswer` input, with the answer\n * encoded by the block's answer schema, and passes it to the `respond` option to submit.\n *\n * The returned function returns `undefined` for a message without a block, or with a block it\n * can't decode or has no renderer for.\n *\n * **Example** (Rendering blocks with React)\n *\n * ```tsx\n * import { BlockKit } from \"effect-assistant\"\n * import type { ReactNode } from \"react\"\n *\n * const Blocks = BlockKit.make([PickOne, OrderDraft])\n *\n * const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {\n * pick_one: ({ props, open, respond }) =>\n * props.options.map((option) => (\n * <button key={option} disabled={!open} onClick={() => respond({ picked: option })}>\n * {option}\n * </button>\n * )),\n * // No answer schema: the button calls the app's own API with the props the user sees.\n * order_draft: ({ props }) => <button onClick={() => placeOrder(props)}>Place order</button>\n * })\n *\n * // For each AssistantMessage in the chat's timeline:\n * renderBlock(message, { respond: (answer) => submit(answer) })\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport function toClient<Kit extends BlockKit, Out>(\n kit: Kit,\n renderers: Renderers<Kit, Out>\n): (message: Timeline.AssistantMessage, options: RenderOptions) => Out | undefined\n// The overload above checks that every kind has a renderer; here each one takes any block's\n// arguments.\nexport function toClient<Out>(\n kit: BlockKit,\n renderers: Readonly<Record<string, (args: RenderArgs<Block.Any>) => Out>>\n): (message: Timeline.AssistantMessage, options: RenderOptions) => Out | undefined {\n return (message, options) => {\n if (message.block === undefined) return undefined\n\n const { kind } = message.block\n const block = kit.byKind.get(kind)\n const render = renderers[kind]\n\n if (block === undefined || render === undefined) return undefined\n\n const props = Schema.decodeUnknownOption(block.props)(message.block.props)\n\n if (Option.isNone(props)) return undefined\n\n const given = message.block.answer\n const answerCodec = block.answer === undefined ? undefined : Schema.toCodecJson(block.answer)\n\n const answer =\n given === undefined || answerCodec === undefined ? Option.none() : Schema.decodeUnknownOption(answerCodec)(given)\n\n return render({\n messageId: message.id,\n text: message.text,\n props: props.value,\n open: message.block.status === \"open\",\n answer: Option.getOrUndefined(answer),\n respond: (value) =>\n options.respond(\n AssistantEvent.BlockAnswer.make({\n // A block takes one answer: the same id every time, so a second click is stored once.\n id: InputId.fromKey(`${message.id}:answer`),\n messageId: message.id,\n kind,\n value: answerCodec === undefined ? value : Schema.encodeUnknownSync(answerCodec)(value)\n })\n )\n })\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlockKit.js","names":["Block.isToolResult","AssistantEvent.isLibraryEvent","InputId.fromKey"],"sources":["../src/BlockKit.ts"],"sourcesContent":["/**\n * Groups the blocks an assistant may put in its messages, and renders them on the client.\n *\n * A kit is created with {@link make} and passed to `Assistant.make`. {@link fromEntries} computes\n * the state of every message's block from a chat's log, {@link closeStale} closes the blocks the\n * user moved on from, and {@link toClient} creates a client-side renderer with one function per\n * block kind.\n *\n * @since 0.1.0\n */\nimport type * as Tool from \"effect/ai/Tool\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport * as Block from \"./Block.ts\"\nimport type * as Timeline from \"./Timeline.ts\"\nimport { CurrentChat } from \"./CurrentChat.ts\"\nimport * as InputId from \"./InputId.ts\"\n\n/**\n * Represents a catalog of blocks, with a lookup by kind.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface BlockKit<Blocks extends Block.Any = Block.Any> {\n readonly blocks: ReadonlyArray<Blocks>\n readonly byKind: ReadonlyMap<string, Blocks>\n}\n\n/**\n * Creates a kit from blocks.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <const Blocks extends ReadonlyArray<Block.Any>>(blocks: Blocks): BlockKit<Blocks[number]> => ({\n blocks,\n byKind: new Map(blocks.map((block) => [block.kind, block]))\n})\n\n/**\n * Returns the names of the tools whose results the kit's blocks show (`Block.fromToolResult`).\n *\n * @category utils\n * @since 0.5.0\n */\nexport const toolNames = (kit: BlockKit | undefined): ReadonlySet<string> =>\n new Set((kit?.blocks ?? []).flatMap((block) => (Block.isToolResult(block) ? [block.tool.name] : [])))\n\ntype KindOf<B> = B extends { readonly kind: infer K extends string } ? K : never\n\ntype BlockOf<Kit extends BlockKit, K> = Extract<Kit[\"blocks\"][number], { readonly kind: K }>\n\ntype PropsOf<B> = B extends { readonly props: Schema.Top } ? B[\"props\"][\"Type\"] : never\n\ntype AnswerOf<B> = B extends { readonly answer: infer A } ? (A extends Schema.Top ? A[\"Type\"] : never) : never\n\ntype ResultArgs<B> = B extends { readonly tool: infer T }\n ? {\n /** The tool's result the block shows, decoded by its success schema. */\n readonly result: Tool.Success<T>\n }\n : unknown\n\n/**\n * Represents the state of a message's block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface State {\n readonly open: boolean\n /** The user's answer, if they used the block. */\n readonly answer: AssistantEvent.BlockAnswer | undefined\n}\n\n/**\n * Returns the block states after one more event.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const apply = (\n states: ReadonlyMap<string, State>,\n event: { readonly _tag: string }\n): ReadonlyMap<string, State> => {\n if (!AssistantEvent.isLibraryEvent(event)) return states\n\n const close = (ids: ReadonlyArray<string>) => {\n const open = ids.filter((id) => states.get(id)?.open === true)\n\n if (open.length === 0) return states\n\n const next = new Map(states)\n\n for (const id of open) next.set(id, { open: false, answer: undefined })\n\n return next\n }\n\n if (Predicate.isTagged(event, \"BlocksClosed\")) return close(event.messageIds)\n\n if (Predicate.isTagged(event, \"AssistantMessage\")) {\n const closed = close(event.closes ?? [])\n\n return event.block === undefined ? closed : new Map(closed).set(event.id, { open: true, answer: undefined })\n }\n\n if (!Predicate.isTagged(event, \"BlockAnswer\") || states.get(event.messageId)?.open !== true) return states\n\n return new Map(states).set(event.messageId, { open: false, answer: event })\n}\n\n/**\n * Returns the state of every message's block, by message id.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const fromEntries = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyMap<string, State> => {\n let states: ReadonlyMap<string, State> = new Map()\n\n for (const entry of entries) states = apply(states, entry.event)\n\n return states\n}\n\n/**\n * Returns the ids of the messages whose blocks are still open after the user typed the given\n * number of messages without using them.\n *\n * **Details**\n *\n * These are the blocks {@link closeStale} closes.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const staleBlocks = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n options: { readonly messages: number }\n): ReadonlyArray<string> => {\n const open = [...fromEntries(entries)].flatMap(([id, state]) => (state.open ? [id] : []))\n\n return open.filter((id) => {\n const shown = entries.findIndex(\n ({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") && event.id === id\n )\n\n if (shown === -1) return false\n\n const typed = entries.slice(shown + 1).filter(({ event }) => Predicate.isTagged(event, \"UserMessage\")).length\n\n return typed >= options.messages\n })\n}\n\n/**\n * Closes the blocks the user moved on from, which are still open after the given number of typed\n * messages.\n *\n * **Details**\n *\n * Run it from a turn, before or after its loop. It records a `BlocksClosed` event, so every\n * client folds the blocks closed.\n *\n * @see {@link staleBlocks} for the blocks it closes\n *\n * @category utils\n * @since 0.1.0\n */\nexport const closeStale = Effect.fn(function* (options: { readonly messages: number }) {\n const chat = yield* CurrentChat\n const stale = staleBlocks(yield* chat.entries, options)\n\n if (stale.length > 0) yield* chat.append(AssistantEvent.BlocksClosed.make({ messageIds: stale }))\n})\n\n/**\n * Represents what a renderer receives for one block. A block that shows a tool's result also gets\n * the `result`.\n *\n * @category models\n * @since 0.1.0\n */\nexport type RenderArgs<B> = BlockArgs<B> & ResultArgs<B>\n\n/**\n * Represents what every block's renderer receives.\n *\n * @category models\n * @since 0.5.0\n */\nexport interface BlockArgs<B> {\n /**\n * The id of the message the block is on. A block whose button calls your API can key the work\n * on it, with the chat's id, so the work runs once per block.\n */\n readonly messageId: string\n readonly text: string\n readonly props: PropsOf<B>\n /**\n * Whether the block is open. It is `false` once the block is answered, with the block or in\n * words; show it read-only then.\n */\n readonly open: boolean\n /** The user's answer, if they used the block. */\n readonly answer: AnswerOf<B> | undefined\n /** Sends an answer. Only call it while `open`. */\n readonly respond: (answer: AnswerOf<B>) => void\n}\n\n/**\n * Represents a renderer for every kind in a kit.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Renderers<Kit extends BlockKit, Out> = {\n readonly [K in KindOf<Kit[\"blocks\"][number]>]: (args: RenderArgs<BlockOf<Kit, K>>) => Out\n}\n\n/**\n * Represents the options for rendering one message's block.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface RenderOptions {\n /** Submits an answer, for example over RPC. */\n readonly respond: (answer: AssistantEvent.BlockAnswer) => void\n}\n\n/**\n * Creates a client-side renderer for the blocks on the assistant's messages in a chat's timeline\n * (`Timeline.AssistantMessage`).\n *\n * **Details**\n *\n * The renderers are type-checked, so every kind in the kit needs one, and each receives typed\n * `props` and `answer`. A renderer's `respond` builds a `BlockAnswer` input, with the answer\n * encoded by the block's answer schema, and passes it to the `respond` option to submit.\n *\n * The returned function returns `undefined` for a message without a block, or with a block it\n * can't decode or has no renderer for.\n *\n * **Example** (Rendering blocks with React)\n *\n * ```tsx\n * import { BlockKit } from \"effect-assistant\"\n * import type { ReactNode } from \"react\"\n *\n * const Blocks = BlockKit.make([PickOne, OrderDraft])\n *\n * const renderBlock = BlockKit.toClient<typeof Blocks, ReactNode>(Blocks, {\n * pick_one: ({ props, open, respond }) =>\n * props.options.map((option) => (\n * <button key={option} disabled={!open} onClick={() => respond({ picked: option })}>\n * {option}\n * </button>\n * )),\n * // No answer schema: the button calls the app's own API with the props the user sees.\n * order_draft: ({ props }) => <button onClick={() => placeOrder(props)}>Place order</button>\n * })\n *\n * // For each AssistantMessage in the chat's timeline:\n * renderBlock(message, { respond: (answer) => submit(answer) })\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport function toClient<Kit extends BlockKit, Out>(\n kit: Kit,\n renderers: Renderers<Kit, Out>\n): (message: Timeline.AssistantMessage, options: RenderOptions) => Out | undefined\n// The overload above checks that every kind has a renderer; here each one takes any block's\n// arguments.\nexport function toClient<Out>(\n kit: BlockKit,\n renderers: Readonly<Record<string, (args: BlockArgs<Block.Any> & { readonly result?: unknown }) => Out>>\n): (message: Timeline.AssistantMessage, options: RenderOptions) => Out | undefined {\n return (message, options) => {\n if (message.block === undefined) return undefined\n\n const { kind } = message.block\n const block = kit.byKind.get(kind)\n const render = renderers[kind]\n\n if (block === undefined || render === undefined) return undefined\n\n const props = Schema.decodeUnknownOption(block.props)(message.block.props)\n\n if (Option.isNone(props)) return undefined\n\n const result = Block.isToolResult(block)\n ? Schema.decodeUnknownOption(Schema.toCodecJson(block.result))(message.block.result)\n : Option.some(undefined)\n\n if (Option.isNone(result)) return undefined\n\n const given = message.block.answer\n const answerCodec = block.answer === undefined ? undefined : Schema.toCodecJson(block.answer)\n\n const answer =\n given === undefined || answerCodec === undefined ? Option.none() : Schema.decodeUnknownOption(answerCodec)(given)\n\n return render({\n messageId: message.id,\n text: message.text,\n props: props.value,\n result: result.value,\n open: message.block.status === \"open\",\n answer: Option.getOrUndefined(answer),\n respond: (value) =>\n options.respond(\n AssistantEvent.BlockAnswer.make({\n // A block takes one answer: the same id every time, so a second click is stored once.\n id: InputId.fromKey(`${message.id}:answer`),\n messageId: message.id,\n kind,\n value: answerCodec === undefined ? value : Schema.encodeUnknownSync(answerCodec)(value)\n })\n )\n })\n }\n}\n"],"mappings":"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"}
|
package/dist/Compaction.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface Summary {
|
|
|
29
29
|
export declare const inputTokens: (entries: ReadonlyArray<AnyEntry>) => number;
|
|
30
30
|
/**
|
|
31
31
|
* Returns the items that were open at `throughSeq` and are still open: blocks the user hasn't
|
|
32
|
-
* answered,
|
|
32
|
+
* answered, specialists that are working, and tool calls waiting for the user's approval. The result is text for
|
|
33
33
|
* the model, or `None` when nothing is open.
|
|
34
34
|
*
|
|
35
35
|
* @category utils
|
package/dist/Compaction.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Compaction.d.ts","names":[],"sources":["../src/Compaction.ts"],"mappings":";;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Compaction.d.ts","names":[],"sources":["../src/Compaction.ts"],"mappings":";;;;;;;;;;;;;;;;iBAgCiB;WACN;WACA;WACA;;;;;;;;;qBAUE,cAAW,SAAa,cAAc;;;;;;;;;qBAmBtC,YAAS,SACX,cAAc,WAAwB,uBAE9C,OAAO;;;;;;;;qBAmEG,gBAAa,SACf,cAAc,WAAwB,uBAE9C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmEG,QAAK,YACH,mBAAmB,SAAS,aACxC;WAA0B;GAAc,SAC9B,cAAc,MAAgB,aACtC,cAAc,MAAgB,UAAU,OAAO"}
|