effect-assistant 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -2
- package/dist/AppEvent.d.ts +111 -0
- package/dist/AppEvent.d.ts.map +1 -0
- package/dist/AppEvent.js +71 -0
- package/dist/AppEvent.js.map +1 -0
- package/dist/Assistant.d.ts +403 -0
- package/dist/Assistant.d.ts.map +1 -0
- package/dist/Assistant.js +126 -0
- package/dist/Assistant.js.map +1 -0
- package/dist/AssistantActivity.d.ts +85 -0
- package/dist/AssistantActivity.d.ts.map +1 -0
- package/dist/AssistantActivity.js +105 -0
- package/dist/AssistantActivity.js.map +1 -0
- package/dist/AssistantEngine.d.ts +116 -0
- package/dist/AssistantEngine.d.ts.map +1 -0
- package/dist/AssistantEngine.js +255 -0
- package/dist/AssistantEngine.js.map +1 -0
- package/dist/AssistantEvent.d.ts +551 -0
- package/dist/AssistantEvent.d.ts.map +1 -0
- package/dist/AssistantEvent.js +473 -0
- package/dist/AssistantEvent.js.map +1 -0
- package/dist/AssistantLoop.d.ts +173 -0
- package/dist/AssistantLoop.d.ts.map +1 -0
- package/dist/AssistantLoop.js +111 -0
- package/dist/AssistantLoop.js.map +1 -0
- package/dist/AssistantToolCall.d.ts +32 -0
- package/dist/AssistantToolCall.d.ts.map +1 -0
- package/dist/AssistantToolCall.js +37 -0
- package/dist/AssistantToolCall.js.map +1 -0
- package/dist/Block.d.ts +69 -0
- package/dist/Block.d.ts.map +1 -0
- package/dist/Block.js +39 -0
- package/dist/Block.js.map +1 -0
- package/dist/BlockKit.d.ts +180 -0
- package/dist/BlockKit.d.ts.map +1 -0
- package/dist/BlockKit.js +148 -0
- package/dist/BlockKit.js.map +1 -0
- package/dist/ChatId.d.ts +31 -0
- package/dist/ChatId.d.ts.map +1 -0
- package/dist/ChatId.js +36 -0
- package/dist/ChatId.js.map +1 -0
- package/dist/ChatLog.d.ts +109 -0
- package/dist/ChatLog.d.ts.map +1 -0
- package/dist/ChatLog.js +177 -0
- package/dist/ChatLog.js.map +1 -0
- package/dist/ChatView.d.ts +408 -0
- package/dist/ChatView.d.ts.map +1 -0
- package/dist/ChatView.js +443 -0
- package/dist/ChatView.js.map +1 -0
- package/dist/Chats.d.ts +110 -0
- package/dist/Chats.d.ts.map +1 -0
- package/dist/Chats.js +98 -0
- package/dist/Chats.js.map +1 -0
- package/dist/Compaction.d.ts +105 -0
- package/dist/Compaction.d.ts.map +1 -0
- package/dist/Compaction.js +126 -0
- package/dist/Compaction.js.map +1 -0
- package/dist/CurrentChat.d.ts +40 -0
- package/dist/CurrentChat.d.ts.map +1 -0
- package/dist/CurrentChat.js +33 -0
- package/dist/CurrentChat.js.map +1 -0
- package/dist/InputId.d.ts +38 -0
- package/dist/InputId.d.ts.map +1 -0
- package/dist/InputId.js +43 -0
- package/dist/InputId.js.map +1 -0
- package/dist/Instructions.d.ts +56 -0
- package/dist/Instructions.d.ts.map +1 -0
- package/dist/Instructions.js +50 -0
- package/dist/Instructions.js.map +1 -0
- package/dist/Memory.d.ts +190 -0
- package/dist/Memory.d.ts.map +1 -0
- package/dist/Memory.js +216 -0
- package/dist/Memory.js.map +1 -0
- package/dist/RunId.d.ts +41 -0
- package/dist/RunId.d.ts.map +1 -0
- package/dist/RunId.js +46 -0
- package/dist/RunId.js.map +1 -0
- package/dist/Skill.d.ts +72 -0
- package/dist/Skill.d.ts.map +1 -0
- package/dist/Skill.js +58 -0
- package/dist/Skill.js.map +1 -0
- package/dist/Specialist.d.ts +267 -0
- package/dist/Specialist.d.ts.map +1 -0
- package/dist/Specialist.js +119 -0
- package/dist/Specialist.js.map +1 -0
- package/dist/ToolDiscovery.d.ts +107 -0
- package/dist/ToolDiscovery.d.ts.map +1 -0
- package/dist/ToolDiscovery.js +140 -0
- package/dist/ToolDiscovery.js.map +1 -0
- package/dist/ToolFailure.d.ts +66 -0
- package/dist/ToolFailure.d.ts.map +1 -0
- package/dist/ToolFailure.js +79 -0
- package/dist/ToolFailure.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/internal/chat.js +177 -0
- package/dist/internal/chat.js.map +1 -0
- package/dist/internal/events.d.ts +46 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +65 -0
- package/dist/internal/events.js.map +1 -0
- package/dist/internal/history.js +147 -0
- package/dist/internal/history.js.map +1 -0
- package/dist/internal/instructions.js +11 -0
- package/dist/internal/instructions.js.map +1 -0
- package/dist/internal/loop.d.ts +73 -0
- package/dist/internal/loop.d.ts.map +1 -0
- package/dist/internal/loop.js +131 -0
- package/dist/internal/loop.js.map +1 -0
- package/dist/internal/schema.js +14 -0
- package/dist/internal/schema.js.map +1 -0
- package/dist/internal/skills.js +28 -0
- package/dist/internal/skills.js.map +1 -0
- package/dist/internal/specialists.js +272 -0
- package/dist/internal/specialists.js.map +1 -0
- package/dist/internal/telemetry.js +85 -0
- package/dist/internal/telemetry.js.map +1 -0
- package/dist/internal/toolkit.d.ts +62 -0
- package/dist/internal/toolkit.d.ts.map +1 -0
- package/dist/internal/toolkit.js +108 -0
- package/dist/internal/toolkit.js.map +1 -0
- package/dist/internal/tools.js +113 -0
- package/dist/internal/tools.js.map +1 -0
- package/dist/internal/turns.d.ts +64 -0
- package/dist/internal/turns.d.ts.map +1 -0
- package/dist/internal/turns.js +214 -0
- package/dist/internal/turns.js.map +1 -0
- package/dist/internal/uuid.js +45 -0
- package/dist/internal/uuid.js.map +1 -0
- package/dist/internal/writer.d.ts +12 -0
- package/dist/internal/writer.d.ts.map +1 -0
- package/package.json +52 -3
- package/src/AppEvent.ts +134 -0
- package/src/Assistant.ts +589 -0
- package/src/AssistantActivity.ts +150 -0
- package/src/AssistantEngine.ts +343 -0
- package/src/AssistantEvent.ts +569 -0
- package/src/AssistantLoop.ts +209 -0
- package/src/AssistantToolCall.ts +40 -0
- package/src/Block.ts +84 -0
- package/src/BlockKit.ts +298 -0
- package/src/ChatId.ts +36 -0
- package/src/ChatLog.ts +247 -0
- package/src/ChatView.ts +587 -0
- package/src/Chats.ts +122 -0
- package/src/Compaction.ts +207 -0
- package/src/CurrentChat.ts +47 -0
- package/src/InputId.ts +43 -0
- package/src/Instructions.ts +72 -0
- package/src/Memory.ts +276 -0
- package/src/RunId.ts +46 -0
- package/src/Skill.ts +91 -0
- package/src/Specialist.ts +355 -0
- package/src/ToolDiscovery.ts +179 -0
- package/src/ToolFailure.ts +74 -0
- package/src/index.ts +120 -0
- package/src/internal/chat.ts +323 -0
- package/src/internal/events.ts +100 -0
- package/src/internal/history.ts +294 -0
- package/src/internal/instructions.ts +9 -0
- package/src/internal/loop.ts +274 -0
- package/src/internal/schema.ts +25 -0
- package/src/internal/skills.ts +37 -0
- package/src/internal/specialists.ts +410 -0
- package/src/internal/telemetry.ts +125 -0
- package/src/internal/toolkit.ts +188 -0
- package/src/internal/tools.ts +159 -0
- package/src/internal/turns.ts +410 -0
- package/src/internal/uuid.ts +50 -0
- package/src/internal/writer.ts +12 -0
package/dist/Chats.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { ChatId } from "./ChatId.js";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
//#region src/Chats.ts
|
|
7
|
+
/**
|
|
8
|
+
* This module provides the `Chats` service, through which the app tells the library which chats
|
|
9
|
+
* exist.
|
|
10
|
+
*
|
|
11
|
+
* The app keeps its own record of each chat (its owner, its title, whether it was deleted) and
|
|
12
|
+
* decides when a chat is created: up front, or on its first message. The library asks `Chats`
|
|
13
|
+
* before it stores an input and before each model call. When the app reports a chat as deleted,
|
|
14
|
+
* the library deletes it as well (see `Assistant.delete`).
|
|
15
|
+
*
|
|
16
|
+
* @since 0.1.0
|
|
17
|
+
*/
|
|
18
|
+
var Chats_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
+
Chats: () => Chats,
|
|
20
|
+
GetChatError: () => GetChatError,
|
|
21
|
+
Reason: () => Reason
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Schema for the reason a chat can't be used. `"NotFound"` means the app doesn't know the chat,
|
|
25
|
+
* and `"Deleted"` means the app deleted it.
|
|
26
|
+
*
|
|
27
|
+
* @category schemas
|
|
28
|
+
* @since 0.1.0
|
|
29
|
+
*/
|
|
30
|
+
const Reason = Schema.Literals(["NotFound", "Deleted"]);
|
|
31
|
+
/**
|
|
32
|
+
* Represents the error that `get` fails with when a chat can't be used. It carries the chat's id
|
|
33
|
+
* and the {@link Reason}. An input for such a chat isn't stored.
|
|
34
|
+
*
|
|
35
|
+
* **Example** (Failing for an unknown chat)
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* if (row === undefined) return yield* new GetChatError({ chatId, reason: "NotFound" })
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @category errors
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
*/
|
|
44
|
+
var GetChatError = class extends Schema.Error("effect-assistant/Chats/GetChatError")({
|
|
45
|
+
_tag: Schema.tag("GetChatError"),
|
|
46
|
+
chatId: ChatId,
|
|
47
|
+
reason: Reason
|
|
48
|
+
}) {};
|
|
49
|
+
/**
|
|
50
|
+
* Service that tells the library which of the app's chats exist.
|
|
51
|
+
*
|
|
52
|
+
* **Details**
|
|
53
|
+
*
|
|
54
|
+
* By default every chat exists from its first input, and a chat is deleted only when its log
|
|
55
|
+
* says so (`ChatDeleted`). Provide your own implementation to have the app decide. Provide it
|
|
56
|
+
* where you provide the `ChatLog`, so the engine and your server see the same one.
|
|
57
|
+
*
|
|
58
|
+
* **Example** (Reading chats from the app's own table)
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { type ChatId, Chats } from "effect-assistant"
|
|
62
|
+
* import * as Effect from "effect/Effect"
|
|
63
|
+
* import * as Layer from "effect/Layer"
|
|
64
|
+
* import * as SqlClient from "effect/sql/SqlClient"
|
|
65
|
+
*
|
|
66
|
+
* const ChatsLive = Layer.effect(
|
|
67
|
+
* Chats.Chats,
|
|
68
|
+
* Effect.gen(function* () {
|
|
69
|
+
* const sql = yield* SqlClient.SqlClient
|
|
70
|
+
*
|
|
71
|
+
* return {
|
|
72
|
+
* get: Effect.fn(function* (chatId: ChatId.ChatId) {
|
|
73
|
+
* const [row] = yield* sql<{ readonly deleted_at: number | null }>`
|
|
74
|
+
* SELECT deleted_at FROM chats WHERE chat_id = ${chatId}
|
|
75
|
+
* `.pipe(Effect.orDie)
|
|
76
|
+
*
|
|
77
|
+
* if (row === undefined) {
|
|
78
|
+
* return yield* new Chats.GetChatError({ chatId, reason: "NotFound" })
|
|
79
|
+
* }
|
|
80
|
+
* if (row.deleted_at !== null) {
|
|
81
|
+
* return yield* new Chats.GetChatError({ chatId, reason: "Deleted" })
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* return { chatId }
|
|
85
|
+
* })
|
|
86
|
+
* }
|
|
87
|
+
* })
|
|
88
|
+
* )
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @category services
|
|
92
|
+
* @since 0.1.0
|
|
93
|
+
*/
|
|
94
|
+
const Chats = Context.Reference("effect-assistant/Chats", { defaultValue: () => ({ get: (chatId) => Effect.succeed({ chatId }) }) });
|
|
95
|
+
//#endregion
|
|
96
|
+
export { Chats, Chats_exports, GetChatError, Reason };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=Chats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Chats.js","names":[],"sources":["../src/Chats.ts"],"sourcesContent":["/**\n * This module provides the `Chats` service, through which the app tells the library which chats\n * exist.\n *\n * The app keeps its own record of each chat (its owner, its title, whether it was deleted) and\n * decides when a chat is created: up front, or on its first message. The library asks `Chats`\n * before it stores an input and before each model call. When the app reports a chat as deleted,\n * the library deletes it as well (see `Assistant.delete`).\n *\n * @since 0.1.0\n */\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport { ChatId } from \"./ChatId.ts\"\n\n/**\n * Schema for the reason a chat can't be used. `\"NotFound\"` means the app doesn't know the chat,\n * and `\"Deleted\"` means the app deleted it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Reason = Schema.Literals([\"NotFound\", \"Deleted\"])\n\n/**\n * The reason a chat can't be used.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Reason = typeof Reason.Type\n\n/**\n * Represents the error that `get` fails with when a chat can't be used. It carries the chat's id\n * and the {@link Reason}. An input for such a chat isn't stored.\n *\n * **Example** (Failing for an unknown chat)\n *\n * ```ts\n * if (row === undefined) return yield* new GetChatError({ chatId, reason: \"NotFound\" })\n * ```\n *\n * @category errors\n * @since 0.1.0\n */\nexport class GetChatError extends Schema.Error<GetChatError>(\"effect-assistant/Chats/GetChatError\")({\n _tag: Schema.tag(\"GetChatError\"),\n chatId: ChatId,\n reason: Reason\n}) {}\n\n/**\n * Represents what the library needs to know about a chat, which is its id. The app's own record\n * can carry more.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ChatRecord {\n readonly chatId: ChatId\n}\n\n/**\n * Represents the app's chats, as the `Chats` service exposes them.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n /** Returns the chat, or fails with a `GetChatError` that says why it can't be used. */\n readonly get: (chatId: ChatId) => Effect.Effect<ChatRecord, GetChatError>\n}\n\n/**\n * Service that tells the library which of the app's chats exist.\n *\n * **Details**\n *\n * By default every chat exists from its first input, and a chat is deleted only when its log\n * says so (`ChatDeleted`). Provide your own implementation to have the app decide. Provide it\n * where you provide the `ChatLog`, so the engine and your server see the same one.\n *\n * **Example** (Reading chats from the app's own table)\n *\n * ```ts\n * import { type ChatId, Chats } from \"effect-assistant\"\n * import * as Effect from \"effect/Effect\"\n * import * as Layer from \"effect/Layer\"\n * import * as SqlClient from \"effect/sql/SqlClient\"\n *\n * const ChatsLive = Layer.effect(\n * Chats.Chats,\n * Effect.gen(function* () {\n * const sql = yield* SqlClient.SqlClient\n *\n * return {\n * get: Effect.fn(function* (chatId: ChatId.ChatId) {\n * const [row] = yield* sql<{ readonly deleted_at: number | null }>`\n * SELECT deleted_at FROM chats WHERE chat_id = ${chatId}\n * `.pipe(Effect.orDie)\n *\n * if (row === undefined) {\n * return yield* new Chats.GetChatError({ chatId, reason: \"NotFound\" })\n * }\n * if (row.deleted_at !== null) {\n * return yield* new Chats.GetChatError({ chatId, reason: \"Deleted\" })\n * }\n *\n * return { chatId }\n * })\n * }\n * })\n * )\n * ```\n *\n * @category services\n * @since 0.1.0\n */\nexport const Chats: Context.Reference<Service> = Context.Reference<Service>(\"effect-assistant/Chats\", {\n defaultValue: () => ({ get: (chatId) => Effect.succeed({ chatId }) })\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,SAAS,OAAO,SAAS,CAAC,YAAY,SAAS,CAAC;;;;;;;;;;;;;;AAuB7D,IAAa,eAAb,cAAkC,OAAO,MAAoB,qCAAqC,CAAC,CAAC;CAClG,MAAM,OAAO,IAAI,cAAc;CAC/B,QAAQ;CACR,QAAQ;AACV,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEJ,MAAa,QAAoC,QAAQ,UAAmB,0BAA0B,EACpG,qBAAqB,EAAE,MAAM,WAAW,OAAO,QAAQ,EAAE,OAAO,CAAC,EAAE,GACrE,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AnyEntry, AssistantEvent_d_exports } from "./AssistantEvent.js";
|
|
2
|
+
import { Assistant_d_exports, Entry } from "./Assistant.js";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Prompt from "effect/ai/Prompt";
|
|
5
|
+
declare namespace Compaction_d_exports {
|
|
6
|
+
export { Summary, attachedFiles, inputTokens, openItems, since };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Represents the fields a summary event must have: its text and the last `seq` it covers.
|
|
10
|
+
*
|
|
11
|
+
* Entries after `throughSeq` are kept as they are, so an input that arrived while the summary was
|
|
12
|
+
* being written isn't lost.
|
|
13
|
+
*
|
|
14
|
+
* @category models
|
|
15
|
+
* @since 0.1.0
|
|
16
|
+
*/
|
|
17
|
+
export interface Summary {
|
|
18
|
+
readonly _tag: string;
|
|
19
|
+
readonly text: string;
|
|
20
|
+
readonly throughSeq: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns the number of input tokens used by the chat's last model call, which shows how large
|
|
24
|
+
* the prompt has grown. Returns 0 before the first call.
|
|
25
|
+
*
|
|
26
|
+
* @category utils
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
export declare const inputTokens: (entries: ReadonlyArray<AnyEntry>) => number;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the items that were open at `throughSeq` and are still open: blocks the user hasn't
|
|
32
|
+
* answered, and specialists that are working. The result is text for
|
|
33
|
+
* the model, or `None` when nothing is open.
|
|
34
|
+
*
|
|
35
|
+
* @category utils
|
|
36
|
+
* @since 0.1.0
|
|
37
|
+
*/
|
|
38
|
+
export declare const openItems: (entries: ReadonlyArray<AnyEntry>, throughSeq: number) => Option.Option<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Returns the files the user attached up to `throughSeq`, as lines of text for the model, or
|
|
41
|
+
* `None` when there are none. A summary may leave out a file's id, which tools need.
|
|
42
|
+
*
|
|
43
|
+
* @category utils
|
|
44
|
+
* @since 0.1.0
|
|
45
|
+
*/
|
|
46
|
+
export declare const attachedFiles: (entries: ReadonlyArray<AnyEntry>, throughSeq: number) => Option.Option<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a history view that starts at the last summary.
|
|
49
|
+
*
|
|
50
|
+
* **Details**
|
|
51
|
+
*
|
|
52
|
+
* `isSummary` identifies the app's summary events. The model reads the last summary, the files
|
|
53
|
+
* attached earlier ({@link attachedFiles}), and the items that are still open
|
|
54
|
+
* ({@link openItems}), followed by the entries after the summary. When the chat has no summary,
|
|
55
|
+
* the view returns every entry.
|
|
56
|
+
*
|
|
57
|
+
* **Example** (Compacting a long chat after each turn)
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { AppEvent, Assistant, type AssistantEvent, Compaction } from "effect-assistant"
|
|
61
|
+
* import * as LanguageModel from "effect/ai/LanguageModel"
|
|
62
|
+
* import * as Effect from "effect/Effect"
|
|
63
|
+
* import * as Schema from "effect/Schema"
|
|
64
|
+
* import * as Stream from "effect/Stream"
|
|
65
|
+
*
|
|
66
|
+
* class Summary extends Schema.TaggedClass<Summary>()("Summary", {
|
|
67
|
+
* text: Schema.String,
|
|
68
|
+
* throughSeq: Schema.Int
|
|
69
|
+
* }) {}
|
|
70
|
+
*
|
|
71
|
+
* declare const transcript: (entries: ReadonlyArray<AssistantEvent.AnyEntry>) => string
|
|
72
|
+
*
|
|
73
|
+
* export const MyAssistant = Assistant.make("my-assistant", { events: [AppEvent.make(Summary)] })
|
|
74
|
+
*
|
|
75
|
+
* const MyAssistantLive = MyAssistant.toLayer((turn) => {
|
|
76
|
+
* const compact = Effect.gen(function* () {
|
|
77
|
+
* const chat = yield* MyAssistant.Chat
|
|
78
|
+
* const entries = yield* chat.entries
|
|
79
|
+
*
|
|
80
|
+
* if (Compaction.inputTokens(entries) < 100_000) return
|
|
81
|
+
*
|
|
82
|
+
* const { text } = yield* LanguageModel.generateText({
|
|
83
|
+
* prompt: `Summarize this chat:\n\n${transcript(entries)}`
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* yield* chat.append(Summary.make({ text, throughSeq: entries.at(-1)?.seq ?? 0 }))
|
|
87
|
+
* })
|
|
88
|
+
*
|
|
89
|
+
* // After the reply, so the user doesn't wait for the summary.
|
|
90
|
+
* return Stream.concat(
|
|
91
|
+
* turn.loop({ instructions: "Be brief.", history: Compaction.since(Schema.is(Summary)) }),
|
|
92
|
+
* Stream.drain(Stream.fromEffect(compact))
|
|
93
|
+
* )
|
|
94
|
+
* })
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @category utils
|
|
98
|
+
* @since 0.1.0
|
|
99
|
+
*/
|
|
100
|
+
export declare const since: (isSummary: (event: unknown) => event is Summary) => <Events extends {
|
|
101
|
+
readonly _tag: string;
|
|
102
|
+
}>(entries: ReadonlyArray<Entry<Events>>) => ReadonlyArray<Entry<Events> | Prompt.Message>;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { Compaction_d_exports };
|
|
105
|
+
//# sourceMappingURL=Compaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Compaction.d.ts","names":[],"sources":["../src/Compaction.ts"],"mappings":";;;;;;;;;;;;;;;;iBA8BiB;WACN;WACA;WACA;;;;;;;;;qBAUE,cAAW,SAAa,cAAc;;;;;;;;;qBAmBtC,YAAS,SACX,cAAc,WAAwB,uBAE9C,OAAO;;;;;;;;qBAoDG,gBAAa,SACf,cAAc,WAAwB,uBAE9C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA+DG,QAAK,YACH,mBAAmB,SAAS,aACxC;WAA0B;GAAc,SAC9B,cAAc,MAAgB,aACtC,cAAc,MAAgB,UAAU,OAAO"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { attachmentLine, isLibraryEvent } from "./AssistantEvent.js";
|
|
3
|
+
import { userText } from "./internal/history.js";
|
|
4
|
+
import { fromEntries } from "./AssistantActivity.js";
|
|
5
|
+
import { fromEntries as fromEntries$1 } from "./BlockKit.js";
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
//#region src/Compaction.ts
|
|
9
|
+
var Compaction_exports = /* @__PURE__ */ __exportAll({
|
|
10
|
+
attachedFiles: () => attachedFiles,
|
|
11
|
+
inputTokens: () => inputTokens,
|
|
12
|
+
openItems: () => openItems,
|
|
13
|
+
since: () => since
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Returns the number of input tokens used by the chat's last model call, which shows how large
|
|
17
|
+
* the prompt has grown. Returns 0 before the first call.
|
|
18
|
+
*
|
|
19
|
+
* @category utils
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
*/
|
|
22
|
+
const inputTokens = (entries) => {
|
|
23
|
+
const last = entries.findLast(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "ModelOutput") && event.usage !== void 0)?.event;
|
|
24
|
+
return last !== void 0 && isLibraryEvent(last) && Predicate.isTagged(last, "ModelOutput") ? last.usage?.inputTokens.total ?? 0 : 0;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Returns the items that were open at `throughSeq` and are still open: blocks the user hasn't
|
|
28
|
+
* answered, and specialists that are working. The result is text for
|
|
29
|
+
* the model, or `None` when nothing is open.
|
|
30
|
+
*
|
|
31
|
+
* @category utils
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
*/
|
|
34
|
+
const openItems = (entries, throughSeq) => {
|
|
35
|
+
const seqOf = (found) => entries.find(({ event }) => found(event))?.seq ?? Number.POSITIVE_INFINITY;
|
|
36
|
+
const opened = (seq) => seq <= throughSeq;
|
|
37
|
+
const blocks = [...fromEntries$1(entries)].flatMap(([messageId, state]) => {
|
|
38
|
+
const shown = entries.find(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "AssistantMessage") && event.id === messageId);
|
|
39
|
+
const message = shown?.event;
|
|
40
|
+
return state.open && shown !== void 0 && opened(shown.seq) && message !== void 0 && isLibraryEvent(message) && Predicate.isTagged(message, "AssistantMessage") ? [`- message ${messageId}, a ${message.block?.kind} block: ${message.text}`] : [];
|
|
41
|
+
});
|
|
42
|
+
const specialists = [...fromEntries(entries).specialists.values()].flatMap((run) => opened(seqOf((event) => isLibraryEvent(event) && Predicate.isTagged(event, "SpecialistStarted") && event.runId === run.runId)) ? [`- your ${run.specialist} specialist (${run.runId}), working on: ${run.goal}`] : []);
|
|
43
|
+
const sections = [blocks.length > 0 ? `Blocks the user hasn't answered:\n${blocks.join("\n")}` : "", specialists.length > 0 ? `Specialists still working:\n${specialists.join("\n")}` : ""].filter((section) => section.length > 0);
|
|
44
|
+
return sections.length === 0 ? Option.none() : Option.some(sections.join("\n\n"));
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Returns the files the user attached up to `throughSeq`, as lines of text for the model, or
|
|
48
|
+
* `None` when there are none. A summary may leave out a file's id, which tools need.
|
|
49
|
+
*
|
|
50
|
+
* @category utils
|
|
51
|
+
* @since 0.1.0
|
|
52
|
+
*/
|
|
53
|
+
const attachedFiles = (entries, throughSeq) => {
|
|
54
|
+
const lines = entries.flatMap(({ seq, event }) => seq <= throughSeq && isLibraryEvent(event) && Predicate.isTagged(event, "UserMessage") ? (event.attachments ?? []).map(attachmentLine) : []);
|
|
55
|
+
return lines.length === 0 ? Option.none() : Option.some(lines.join("\n"));
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Creates a history view that starts at the last summary.
|
|
59
|
+
*
|
|
60
|
+
* **Details**
|
|
61
|
+
*
|
|
62
|
+
* `isSummary` identifies the app's summary events. The model reads the last summary, the files
|
|
63
|
+
* attached earlier ({@link attachedFiles}), and the items that are still open
|
|
64
|
+
* ({@link openItems}), followed by the entries after the summary. When the chat has no summary,
|
|
65
|
+
* the view returns every entry.
|
|
66
|
+
*
|
|
67
|
+
* **Example** (Compacting a long chat after each turn)
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { AppEvent, Assistant, type AssistantEvent, Compaction } from "effect-assistant"
|
|
71
|
+
* import * as LanguageModel from "effect/ai/LanguageModel"
|
|
72
|
+
* import * as Effect from "effect/Effect"
|
|
73
|
+
* import * as Schema from "effect/Schema"
|
|
74
|
+
* import * as Stream from "effect/Stream"
|
|
75
|
+
*
|
|
76
|
+
* class Summary extends Schema.TaggedClass<Summary>()("Summary", {
|
|
77
|
+
* text: Schema.String,
|
|
78
|
+
* throughSeq: Schema.Int
|
|
79
|
+
* }) {}
|
|
80
|
+
*
|
|
81
|
+
* declare const transcript: (entries: ReadonlyArray<AssistantEvent.AnyEntry>) => string
|
|
82
|
+
*
|
|
83
|
+
* export const MyAssistant = Assistant.make("my-assistant", { events: [AppEvent.make(Summary)] })
|
|
84
|
+
*
|
|
85
|
+
* const MyAssistantLive = MyAssistant.toLayer((turn) => {
|
|
86
|
+
* const compact = Effect.gen(function* () {
|
|
87
|
+
* const chat = yield* MyAssistant.Chat
|
|
88
|
+
* const entries = yield* chat.entries
|
|
89
|
+
*
|
|
90
|
+
* if (Compaction.inputTokens(entries) < 100_000) return
|
|
91
|
+
*
|
|
92
|
+
* const { text } = yield* LanguageModel.generateText({
|
|
93
|
+
* prompt: `Summarize this chat:\n\n${transcript(entries)}`
|
|
94
|
+
* })
|
|
95
|
+
*
|
|
96
|
+
* yield* chat.append(Summary.make({ text, throughSeq: entries.at(-1)?.seq ?? 0 }))
|
|
97
|
+
* })
|
|
98
|
+
*
|
|
99
|
+
* // After the reply, so the user doesn't wait for the summary.
|
|
100
|
+
* return Stream.concat(
|
|
101
|
+
* turn.loop({ instructions: "Be brief.", history: Compaction.since(Schema.is(Summary)) }),
|
|
102
|
+
* Stream.drain(Stream.fromEffect(compact))
|
|
103
|
+
* )
|
|
104
|
+
* })
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @category utils
|
|
108
|
+
* @since 0.1.0
|
|
109
|
+
*/
|
|
110
|
+
const since = (isSummary) => (entries) => {
|
|
111
|
+
const last = entries.findLast(({ event }) => isSummary(event))?.event;
|
|
112
|
+
if (last === void 0 || !isSummary(last)) return entries;
|
|
113
|
+
const open = Option.match(openItems(entries, last.throughSeq), {
|
|
114
|
+
onNone: () => "",
|
|
115
|
+
onSome: (items) => `\n\n<still_open>\n${items}\n</still_open>`
|
|
116
|
+
});
|
|
117
|
+
const files = Option.match(attachedFiles(entries, last.throughSeq), {
|
|
118
|
+
onNone: () => "",
|
|
119
|
+
onSome: (lines) => `\n\n<files_attached_earlier>\n${lines}\n</files_attached_earlier>`
|
|
120
|
+
});
|
|
121
|
+
return [userText(`<earlier_in_this_chat>\n${last.text}\n</earlier_in_this_chat>${files}${open}`), ...entries.filter(({ seq, event }) => seq > last.throughSeq && !isSummary(event))];
|
|
122
|
+
};
|
|
123
|
+
//#endregion
|
|
124
|
+
export { Compaction_exports, attachedFiles, inputTokens, openItems, since };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=Compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Compaction.js","names":["AssistantEvent.isLibraryEvent","BlockKit.fromEntries","AssistantActivity.fromEntries","AssistantEvent.attachmentLine"],"sources":["../src/Compaction.ts"],"sourcesContent":["/**\n * This module provides what a long chat needs so that the model can read a summary in place of\n * everything before it.\n *\n * The app decides when to compact, which model writes the summary, and what the summary says.\n * Its handler reads the chat's entries, has a model write a summary, and stores the summary as one\n * of the assistant's own events (see {@link Summary}). Its loop then reads the chat through\n * {@link since}. {@link inputTokens} helps decide when the prompt has grown large enough to\n * compact. The log keeps every entry, so the user still sees the whole chat.\n *\n * @since 0.1.0\n */\nimport type * as Prompt from \"effect/ai/Prompt\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport type * as Assistant from \"./Assistant.ts\"\nimport * as AssistantActivity from \"./AssistantActivity.ts\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport * as BlockKit from \"./BlockKit.ts\"\nimport { userText } from \"./internal/history.ts\"\n\n/**\n * Represents the fields a summary event must have: its text and the last `seq` it covers.\n *\n * Entries after `throughSeq` are kept as they are, so an input that arrived while the summary was\n * being written isn't lost.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Summary {\n readonly _tag: string\n readonly text: string\n readonly throughSeq: number\n}\n\n/**\n * Returns the number of input tokens used by the chat's last model call, which shows how large\n * the prompt has grown. Returns 0 before the first call.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const inputTokens = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): number => {\n const last = entries.findLast(\n ({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"ModelOutput\") && event.usage !== undefined\n )?.event\n\n return last !== undefined && AssistantEvent.isLibraryEvent(last) && Predicate.isTagged(last, \"ModelOutput\")\n ? (last.usage?.inputTokens.total ?? 0)\n : 0\n}\n\n/**\n * Returns the items that were open at `throughSeq` and are still open: blocks the user hasn't\n * answered, and specialists that are working. The result is text for\n * the model, or `None` when nothing is open.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const openItems = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n throughSeq: number\n): Option.Option<string> => {\n const seqOf = (found: (event: AssistantEvent.AnyEntry[\"event\"]) => boolean) =>\n entries.find(({ event }) => found(event))?.seq ?? Number.POSITIVE_INFINITY\n\n const opened = (seq: number) => seq <= throughSeq\n\n const blocks = [...BlockKit.fromEntries(entries)].flatMap(([messageId, state]) => {\n const shown = entries.find(\n ({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") && event.id === messageId\n )\n\n const message = shown?.event\n\n return state.open &&\n shown !== undefined &&\n opened(shown.seq) &&\n message !== undefined &&\n AssistantEvent.isLibraryEvent(message) &&\n Predicate.isTagged(message, \"AssistantMessage\")\n ? [`- message ${messageId}, a ${message.block?.kind} block: ${message.text}`]\n : []\n })\n\n const specialists = [...AssistantActivity.fromEntries(entries).specialists.values()].flatMap((run) =>\n opened(\n seqOf(\n (event) =>\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"SpecialistStarted\") &&\n event.runId === run.runId\n )\n )\n ? [`- your ${run.specialist} specialist (${run.runId}), working on: ${run.goal}`]\n : []\n )\n\n const sections = [\n blocks.length > 0 ? `Blocks the user hasn't answered:\\n${blocks.join(\"\\n\")}` : \"\",\n specialists.length > 0 ? `Specialists still working:\\n${specialists.join(\"\\n\")}` : \"\"\n ].filter((section) => section.length > 0)\n\n return sections.length === 0 ? Option.none() : Option.some(sections.join(\"\\n\\n\"))\n}\n\n/**\n * Returns the files the user attached up to `throughSeq`, as lines of text for the model, or\n * `None` when there are none. A summary may leave out a file's id, which tools need.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const attachedFiles = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n throughSeq: number\n): Option.Option<string> => {\n const lines = entries.flatMap(({ seq, event }) =>\n seq <= throughSeq && AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"UserMessage\")\n ? (event.attachments ?? []).map(AssistantEvent.attachmentLine)\n : []\n )\n\n return lines.length === 0 ? Option.none() : Option.some(lines.join(\"\\n\"))\n}\n\n/**\n * Creates a history view that starts at the last summary.\n *\n * **Details**\n *\n * `isSummary` identifies the app's summary events. The model reads the last summary, the files\n * attached earlier ({@link attachedFiles}), and the items that are still open\n * ({@link openItems}), followed by the entries after the summary. When the chat has no summary,\n * the view returns every entry.\n *\n * **Example** (Compacting a long chat after each turn)\n *\n * ```ts\n * import { AppEvent, Assistant, type AssistantEvent, Compaction } from \"effect-assistant\"\n * import * as LanguageModel from \"effect/ai/LanguageModel\"\n * import * as Effect from \"effect/Effect\"\n * import * as Schema from \"effect/Schema\"\n * import * as Stream from \"effect/Stream\"\n *\n * class Summary extends Schema.TaggedClass<Summary>()(\"Summary\", {\n * text: Schema.String,\n * throughSeq: Schema.Int\n * }) {}\n *\n * declare const transcript: (entries: ReadonlyArray<AssistantEvent.AnyEntry>) => string\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\", { events: [AppEvent.make(Summary)] })\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) => {\n * const compact = Effect.gen(function* () {\n * const chat = yield* MyAssistant.Chat\n * const entries = yield* chat.entries\n *\n * if (Compaction.inputTokens(entries) < 100_000) return\n *\n * const { text } = yield* LanguageModel.generateText({\n * prompt: `Summarize this chat:\\n\\n${transcript(entries)}`\n * })\n *\n * yield* chat.append(Summary.make({ text, throughSeq: entries.at(-1)?.seq ?? 0 }))\n * })\n *\n * // After the reply, so the user doesn't wait for the summary.\n * return Stream.concat(\n * turn.loop({ instructions: \"Be brief.\", history: Compaction.since(Schema.is(Summary)) }),\n * Stream.drain(Stream.fromEffect(compact))\n * )\n * })\n * ```\n *\n * @category utils\n * @since 0.1.0\n */\nexport const since =\n (isSummary: (event: unknown) => event is Summary) =>\n <Events extends { readonly _tag: string }>(\n entries: ReadonlyArray<Assistant.Entry<Events>>\n ): ReadonlyArray<Assistant.Entry<Events> | Prompt.Message> => {\n const last = entries.findLast(({ event }) => isSummary(event))?.event\n\n if (last === undefined || !isSummary(last)) return entries\n\n const open = Option.match(openItems(entries, last.throughSeq), {\n onNone: () => \"\",\n onSome: (items) => `\\n\\n<still_open>\\n${items}\\n</still_open>`\n })\n\n const files = Option.match(attachedFiles(entries, last.throughSeq), {\n onNone: () => \"\",\n onSome: (lines) => `\\n\\n<files_attached_earlier>\\n${lines}\\n</files_attached_earlier>`\n })\n\n return [\n userText(`<earlier_in_this_chat>\\n${last.text}\\n</earlier_in_this_chat>${files}${open}`),\n ...entries.filter(({ seq, event }) => seq > last.throughSeq && !isSummary(event))\n ]\n }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,eAAe,YAA4D;CACtF,MAAM,OAAO,QAAQ,UAClB,EAAE,YACDA,eAA8B,KAAK,KAAK,UAAU,SAAS,OAAO,aAAa,KAAK,MAAM,UAAU,KAAA,CACxG,CAAC,EAAE;CAEH,OAAO,SAAS,KAAA,KAAaA,eAA8B,IAAI,KAAK,UAAU,SAAS,MAAM,aAAa,IACrG,KAAK,OAAO,YAAY,SAAS,IAClC;AACN;;;;;;;;;AAUA,MAAa,aACX,SACA,eAC0B;CAC1B,MAAM,SAAS,UACb,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAK,CAAC,CAAC,EAAE,OAAO,OAAO;CAE3D,MAAM,UAAU,QAAgB,OAAO;CAEvC,MAAM,SAAS,CAAC,GAAGC,cAAqB,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,WAAW;EAChF,MAAM,QAAQ,QAAQ,MACnB,EAAE,YACDD,eAA8B,KAAK,KAAK,UAAU,SAAS,OAAO,kBAAkB,KAAK,MAAM,OAAO,SAC1G;EAEA,MAAM,UAAU,OAAO;EAEvB,OAAO,MAAM,QACX,UAAU,KAAA,KACV,OAAO,MAAM,GAAG,KAChB,YAAY,KAAA,KACZA,eAA8B,OAAO,KACrC,UAAU,SAAS,SAAS,kBAAkB,IAC5C,CAAC,aAAa,UAAU,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,MAAM,IAC1E,CAAC;CACP,CAAC;CAED,MAAM,cAAc,CAAC,GAAGE,YAA8B,OAAO,CAAC,CAAC,YAAY,OAAO,CAAC,CAAC,CAAC,SAAS,QAC5F,OACE,OACG,UACCF,eAA8B,KAAK,KACnC,UAAU,SAAS,OAAO,mBAAmB,KAC7C,MAAM,UAAU,IAAI,KACxB,CACF,IACI,CAAC,UAAU,IAAI,WAAW,eAAe,IAAI,MAAM,iBAAiB,IAAI,MAAM,IAC9E,CAAC,CACP;CAEA,MAAM,WAAW,CACf,OAAO,SAAS,IAAI,qCAAqC,OAAO,KAAK,IAAI,MAAM,IAC/E,YAAY,SAAS,IAAI,+BAA+B,YAAY,KAAK,IAAI,MAAM,EACrF,CAAC,CAAC,QAAQ,YAAY,QAAQ,SAAS,CAAC;CAExC,OAAO,SAAS,WAAW,IAAI,OAAO,KAAK,IAAI,OAAO,KAAK,SAAS,KAAK,MAAM,CAAC;AAClF;;;;;;;;AASA,MAAa,iBACX,SACA,eAC0B;CAC1B,MAAM,QAAQ,QAAQ,SAAS,EAAE,KAAK,YACpC,OAAO,cAAcA,eAA8B,KAAK,KAAK,UAAU,SAAS,OAAO,aAAa,KAC/F,MAAM,eAAe,CAAC,EAAA,CAAG,IAAIG,cAA6B,IAC3D,CAAC,CACP;CAEA,OAAO,MAAM,WAAW,IAAI,OAAO,KAAK,IAAI,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAC1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,SACV,eAEC,YAC4D;CAC5D,MAAM,OAAO,QAAQ,UAAU,EAAE,YAAY,UAAU,KAAK,CAAC,CAAC,EAAE;CAEhE,IAAI,SAAS,KAAA,KAAa,CAAC,UAAU,IAAI,GAAG,OAAO;CAEnD,MAAM,OAAO,OAAO,MAAM,UAAU,SAAS,KAAK,UAAU,GAAG;EAC7D,cAAc;EACd,SAAS,UAAU,qBAAqB,MAAM;CAChD,CAAC;CAED,MAAM,QAAQ,OAAO,MAAM,cAAc,SAAS,KAAK,UAAU,GAAG;EAClE,cAAc;EACd,SAAS,UAAU,iCAAiC,MAAM;CAC5D,CAAC;CAED,OAAO,CACL,SAAS,2BAA2B,KAAK,KAAK,2BAA2B,QAAQ,MAAM,GACvF,GAAG,QAAQ,QAAQ,EAAE,KAAK,YAAY,MAAM,KAAK,cAAc,CAAC,UAAU,KAAK,CAAC,CAClF;AACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ChatId } from "./ChatId.js";
|
|
2
|
+
import { AnyEntry, AssistantEvent_d_exports } from "./AssistantEvent.js";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
declare namespace CurrentChat_d_exports {
|
|
6
|
+
export { CurrentChat };
|
|
7
|
+
}
|
|
8
|
+
declare const CurrentChat_base: Context.ServiceClass<CurrentChat, "effect-assistant/CurrentChat", {
|
|
9
|
+
readonly chatId: ChatId;
|
|
10
|
+
/**
|
|
11
|
+
* The chat's entries so far, in order. They include the library's events and the assistant's
|
|
12
|
+
* own events, decoded; check for these with `Schema.is(MyEvent)` or `instanceof`. An app event
|
|
13
|
+
* that no longer decodes is left out.
|
|
14
|
+
*/
|
|
15
|
+
readonly entries: Effect.Effect<ReadonlyArray<AnyEntry>>;
|
|
16
|
+
/**
|
|
17
|
+
* Appends events after the chat's last entry and sends them to the chat's subscribers. The
|
|
18
|
+
* events can be the library's or the assistant's own (registered with its `events` option),
|
|
19
|
+
* such as a summary. An event that wakes a turn is handled by the next turn.
|
|
20
|
+
*/
|
|
21
|
+
readonly append: (...events: ReadonlyArray<{
|
|
22
|
+
readonly _tag: string;
|
|
23
|
+
}>) => Effect.Effect<void>;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Service that represents the chat the current code runs in.
|
|
27
|
+
*
|
|
28
|
+
* **Details**
|
|
29
|
+
*
|
|
30
|
+
* In a turn, it is the chat the turn runs in. In a specialist's run, and in its handler's build,
|
|
31
|
+
* it is the chat the run belongs to: the chat of the turn that spawned the run. A run's tools
|
|
32
|
+
* read and append to that chat.
|
|
33
|
+
*
|
|
34
|
+
* @category services
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export declare class CurrentChat extends CurrentChat_base {}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { CurrentChat_d_exports };
|
|
40
|
+
//# sourceMappingURL=CurrentChat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurrentChat.d.ts","names":[],"sources":["../src/CurrentChat.ts"],"mappings":";;;;;;;;WAgCqB,QAAA;;;;;;WAMC,SAAA,OAAO,OAAO,cAAc;;;;;;WAM7B,YAAI,QAAQ;aAAyB;SAAoB,OAAO;;;;;;;;;;;;;;qBAfxE,oBAAoB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
//#region src/CurrentChat.ts
|
|
4
|
+
/**
|
|
5
|
+
* This module provides the `CurrentChat` service, which gives code that runs inside a chat
|
|
6
|
+
* access to that chat: its id, its entries, and a way to append to it.
|
|
7
|
+
*
|
|
8
|
+
* A turn's handler, its tools, and its specialists' runs get the chat with `yield* CurrentChat`.
|
|
9
|
+
* Inside a specialist's run, it is the chat the run belongs to, which is the chat of the turn
|
|
10
|
+
* that spawned the run. The service doesn't carry an assistant's event types, so code that
|
|
11
|
+
* doesn't know the assistant, such as a shared tool, can use it. Code that knows the assistant
|
|
12
|
+
* can use `MyAssistant.Chat`, which is the same chat typed with the assistant's events.
|
|
13
|
+
*
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
*/
|
|
16
|
+
var CurrentChat_exports = /* @__PURE__ */ __exportAll({ CurrentChat: () => CurrentChat });
|
|
17
|
+
/**
|
|
18
|
+
* Service that represents the chat the current code runs in.
|
|
19
|
+
*
|
|
20
|
+
* **Details**
|
|
21
|
+
*
|
|
22
|
+
* In a turn, it is the chat the turn runs in. In a specialist's run, and in its handler's build,
|
|
23
|
+
* it is the chat the run belongs to: the chat of the turn that spawned the run. A run's tools
|
|
24
|
+
* read and append to that chat.
|
|
25
|
+
*
|
|
26
|
+
* @category services
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
var CurrentChat = class extends Context.Service()("effect-assistant/CurrentChat") {};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { CurrentChat, CurrentChat_exports };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=CurrentChat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurrentChat.js","names":[],"sources":["../src/CurrentChat.ts"],"sourcesContent":["/**\n * This module provides the `CurrentChat` service, which gives code that runs inside a chat\n * access to that chat: its id, its entries, and a way to append to it.\n *\n * A turn's handler, its tools, and its specialists' runs get the chat with `yield* CurrentChat`.\n * Inside a specialist's run, it is the chat the run belongs to, which is the chat of the turn\n * that spawned the run. The service doesn't carry an assistant's event types, so code that\n * doesn't know the assistant, such as a shared tool, can use it. Code that knows the assistant\n * can use `MyAssistant.Chat`, which is the same chat typed with the assistant's events.\n *\n * @since 0.1.0\n */\nimport * as Context from \"effect/Context\"\nimport type * as Effect from \"effect/Effect\"\nimport type * as AssistantEvent from \"./AssistantEvent.ts\"\nimport type { ChatId } from \"./ChatId.ts\"\n\n/**\n * Service that represents the chat the current code runs in.\n *\n * **Details**\n *\n * In a turn, it is the chat the turn runs in. In a specialist's run, and in its handler's build,\n * it is the chat the run belongs to: the chat of the turn that spawned the run. A run's tools\n * read and append to that chat.\n *\n * @category services\n * @since 0.1.0\n */\nexport class CurrentChat extends Context.Service<\n CurrentChat,\n {\n readonly chatId: ChatId\n /**\n * The chat's entries so far, in order. They include the library's events and the assistant's\n * own events, decoded; check for these with `Schema.is(MyEvent)` or `instanceof`. An app event\n * that no longer decodes is left out.\n */\n readonly entries: Effect.Effect<ReadonlyArray<AssistantEvent.AnyEntry>>\n /**\n * Appends events after the chat's last entry and sends them to the chat's subscribers. The\n * events can be the library's or the assistant's own (registered with its `events` option),\n * such as a summary. An event that wakes a turn is handled by the next turn.\n */\n readonly append: (...events: ReadonlyArray<{ readonly _tag: string }>) => Effect.Effect<void>\n }\n>()(\"effect-assistant/CurrentChat\") {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,cAAb,cAAiC,QAAQ,QAiBvC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
declare namespace InputId_d_exports {
|
|
4
|
+
export { InputId, fromKey, make };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Schema for input ids. Any string is a valid input id, and the schema brands it.
|
|
8
|
+
*
|
|
9
|
+
* @category schemas
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
export declare const InputId: Schema.brand<Schema.String, "~effect-assistant/InputId">;
|
|
13
|
+
/**
|
|
14
|
+
* Represents an input id.
|
|
15
|
+
*
|
|
16
|
+
* @category models
|
|
17
|
+
* @since 0.1.0
|
|
18
|
+
*/
|
|
19
|
+
export type InputId = typeof InputId.Type;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new random input id using Effect's `Random`. Seed `Random` in tests to get the same ids
|
|
22
|
+
* on every run.
|
|
23
|
+
*
|
|
24
|
+
* @category constructors
|
|
25
|
+
* @since 0.1.0
|
|
26
|
+
*/
|
|
27
|
+
export declare const make: () => Effect.Effect<InputId>;
|
|
28
|
+
/**
|
|
29
|
+
* Creates the input id for a key, such as `${runId}:order_placed`. The same key always gives the
|
|
30
|
+
* same id, so an input that is sent again is stored only once.
|
|
31
|
+
*
|
|
32
|
+
* @category constructors
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export declare const fromKey: (key: string) => InputId;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { InputId_d_exports };
|
|
38
|
+
//# sourceMappingURL=InputId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputId.d.ts","names":[],"sources":["../src/InputId.ts"],"mappings":";;;;;;;;;;;qBAgBa,SAAO,OAAA,MAAA,OAAA;;;;;;;YAQR,iBAAiB,QAAQ;;;;;;;;qBASxB,YAAW,OAAO,OAAO;;;;;;;;qBASzB,UAAO,gBAAkB"}
|
package/dist/InputId.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { make as make$1 } from "./internal/uuid.js";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
//#region src/InputId.ts
|
|
6
|
+
/**
|
|
7
|
+
* This module provides the id of an input to an assistant. An input is a user message, a block
|
|
8
|
+
* answer, or one of the app's events, and the id is stored in its `id` field.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
var InputId_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
InputId: () => InputId,
|
|
14
|
+
fromKey: () => fromKey,
|
|
15
|
+
make: () => make
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Schema for input ids. Any string is a valid input id, and the schema brands it.
|
|
19
|
+
*
|
|
20
|
+
* @category schemas
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
*/
|
|
23
|
+
const InputId = Schema.String.pipe(Schema.brand("~effect-assistant/InputId"));
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new random input id using Effect's `Random`. Seed `Random` in tests to get the same ids
|
|
26
|
+
* on every run.
|
|
27
|
+
*
|
|
28
|
+
* @category constructors
|
|
29
|
+
* @since 0.1.0
|
|
30
|
+
*/
|
|
31
|
+
const make = () => Effect.map(make$1, (uuid) => InputId.make(uuid));
|
|
32
|
+
/**
|
|
33
|
+
* Creates the input id for a key, such as `${runId}:order_placed`. The same key always gives the
|
|
34
|
+
* same id, so an input that is sent again is stored only once.
|
|
35
|
+
*
|
|
36
|
+
* @category constructors
|
|
37
|
+
* @since 0.1.0
|
|
38
|
+
*/
|
|
39
|
+
const fromKey = (key) => InputId.make(key);
|
|
40
|
+
//#endregion
|
|
41
|
+
export { InputId, InputId_exports, fromKey, make };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=InputId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InputId.js","names":["Uuid.make"],"sources":["../src/InputId.ts"],"sourcesContent":["/**\n * This module provides the id of an input to an assistant. An input is a user message, a block\n * answer, or one of the app's events, and the id is stored in its `id` field.\n *\n * @since 0.1.0\n */\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport * as Uuid from \"./internal/uuid.ts\"\n\n/**\n * Schema for input ids. Any string is a valid input id, and the schema brands it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const InputId = Schema.String.pipe(Schema.brand(\"~effect-assistant/InputId\"))\n\n/**\n * Represents an input id.\n *\n * @category models\n * @since 0.1.0\n */\nexport type InputId = typeof InputId.Type\n\n/**\n * Creates a new random input id using Effect's `Random`. Seed `Random` in tests to get the same ids\n * on every run.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (): Effect.Effect<InputId> => Effect.map(Uuid.make, (uuid) => InputId.make(uuid))\n\n/**\n * Creates the input id for a key, such as `${runId}:order_placed`. The same key always gives the\n * same id, so an input that is sent again is stored only once.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const fromKey = (key: string): InputId => InputId.make(key)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgBA,MAAa,UAAU,OAAO,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;;;;;;;;AAiBnF,MAAa,aAAqC,OAAO,IAAIA,SAAY,SAAS,QAAQ,KAAK,IAAI,CAAC;;;;;;;;AASpG,MAAa,WAAW,QAAyB,QAAQ,KAAK,GAAG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
declare namespace Instructions_d_exports {
|
|
3
|
+
export { Instructions, Resolved, SpanAttributes, protocol, resolve };
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The text the model has to know for a turn's loop to work.
|
|
7
|
+
*
|
|
8
|
+
* **Details**
|
|
9
|
+
*
|
|
10
|
+
* It tells the model that every step is a tool call, that `wait` (or a last message with
|
|
11
|
+
* `wait: true`) ends the turn, that the user sees only what it sends with `send_user_message`, and
|
|
12
|
+
* that messages can arrive mid-turn. The rest, such as what a `<specialist>` block or an app's
|
|
13
|
+
* event is, is in the tools' descriptions and in the history itself.
|
|
14
|
+
*
|
|
15
|
+
* `turn.instructions(text)` puts it before the text, and a custom loop on `AssistantLoop.run` can
|
|
16
|
+
* use it too.
|
|
17
|
+
*
|
|
18
|
+
* @category utils
|
|
19
|
+
* @since 0.1.0
|
|
20
|
+
*/
|
|
21
|
+
export declare const protocol = "Every step is a tool call. When you have nothing more to do right now, end your turn: set wait to true on your last send_user_message, or call wait if you have nothing to say.\nThe user sees only the text of what you send with send_user_message: anything else you write is never shown, so put the whole answer in the message, not next to it.\nMessages and updates can arrive while you work: handle the newest one, and don't repeat what you already told the user.";
|
|
22
|
+
/**
|
|
23
|
+
* Represents the span attributes added to every model call made with a prompt.
|
|
24
|
+
*
|
|
25
|
+
* @category models
|
|
26
|
+
* @since 0.1.0
|
|
27
|
+
*/
|
|
28
|
+
export type SpanAttributes = Readonly<Record<string, string | number | boolean>>;
|
|
29
|
+
/**
|
|
30
|
+
* Represents instructions resolved to text, with the span attributes to record them under.
|
|
31
|
+
*
|
|
32
|
+
* @category models
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export interface Resolved {
|
|
36
|
+
readonly text: string;
|
|
37
|
+
readonly attributes?: SpanAttributes | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Represents instructions as text, or as an Effect that produces the text (optionally with span
|
|
41
|
+
* attributes).
|
|
42
|
+
*
|
|
43
|
+
* @category models
|
|
44
|
+
* @since 0.1.0
|
|
45
|
+
*/
|
|
46
|
+
export type Instructions<R = never> = string | Effect.Effect<string | Resolved, never, R>;
|
|
47
|
+
/**
|
|
48
|
+
* Resolves instructions to their text and span attributes.
|
|
49
|
+
*
|
|
50
|
+
* @category utils
|
|
51
|
+
* @since 0.1.0
|
|
52
|
+
*/
|
|
53
|
+
export declare const resolve: <R = never>(instructions: Instructions<R>) => Effect.Effect<Resolved, never, R>;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { Instructions_d_exports };
|
|
56
|
+
//# sourceMappingURL=Instructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Instructions.d.ts","names":[],"sources":["../src/Instructions.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;qBA4Ba;;;;;;;YAUD,iBAAiB,SAAS;;;;;;;iBAQrB;WACN;WACA,aAAa;;;;;;;;;YAUZ,aAAa,sBAAsB,OAAO,gBAAgB,iBAAiB;;;;;;;qBAQ1E,UAAuD,WAAC,cAAA,aAAA,OAAA,OAAA,OAAA,iBAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as Predicate from "effect/Predicate";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
//#region src/Instructions.ts
|
|
5
|
+
/**
|
|
6
|
+
* Defines the instructions that tell an assistant, specialist, or skill what to do.
|
|
7
|
+
*
|
|
8
|
+
* Instructions are text, or an Effect that produces text, optionally with span attributes
|
|
9
|
+
* (a {@link Resolved}). {@link resolve} turns them into a `Resolved`, and {@link protocol} is the
|
|
10
|
+
* text a turn's loop needs the model to know.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
var Instructions_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
+
protocol: () => protocol,
|
|
16
|
+
resolve: () => resolve
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* The text the model has to know for a turn's loop to work.
|
|
20
|
+
*
|
|
21
|
+
* **Details**
|
|
22
|
+
*
|
|
23
|
+
* It tells the model that every step is a tool call, that `wait` (or a last message with
|
|
24
|
+
* `wait: true`) ends the turn, that the user sees only what it sends with `send_user_message`, and
|
|
25
|
+
* that messages can arrive mid-turn. The rest, such as what a `<specialist>` block or an app's
|
|
26
|
+
* event is, is in the tools' descriptions and in the history itself.
|
|
27
|
+
*
|
|
28
|
+
* `turn.instructions(text)` puts it before the text, and a custom loop on `AssistantLoop.run` can
|
|
29
|
+
* use it too.
|
|
30
|
+
*
|
|
31
|
+
* @category utils
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
*/
|
|
34
|
+
const protocol = `Every step is a tool call. When you have nothing more to do right now, end your turn: set wait to true on your last send_user_message, or call wait if you have nothing to say.
|
|
35
|
+
The user sees only the text of what you send with send_user_message: anything else you write is never shown, so put the whole answer in the message, not next to it.
|
|
36
|
+
Messages and updates can arrive while you work: handle the newest one, and don't repeat what you already told the user.`;
|
|
37
|
+
/**
|
|
38
|
+
* Resolves instructions to their text and span attributes.
|
|
39
|
+
*
|
|
40
|
+
* @category utils
|
|
41
|
+
* @since 0.1.0
|
|
42
|
+
*/
|
|
43
|
+
const resolve = Effect.fn("Instructions.resolve")(function* (instructions) {
|
|
44
|
+
return Predicate.isString(instructions) ? { text: instructions } : toResolved(yield* instructions);
|
|
45
|
+
});
|
|
46
|
+
const toResolved = (resolved) => Predicate.isString(resolved) ? { text: resolved } : resolved;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { Instructions_exports, protocol, resolve };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=Instructions.js.map
|