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/src/ChatLog.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module provides the `ChatLog` service, which stores each chat as an append-only list of
|
|
3
|
+
* `AssistantEvent`s.
|
|
4
|
+
*
|
|
5
|
+
* A log is built on a {@link Storage}. {@link layerMemory} keeps every chat in memory, for tests
|
|
6
|
+
* and local runs, and {@link layerSql} stores chats in a SQL table so they survive restarts. To
|
|
7
|
+
* use another store, implement a `Storage` and pass it to {@link make}.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*/
|
|
11
|
+
import * as Clock from "effect/Clock"
|
|
12
|
+
import * as Context from "effect/Context"
|
|
13
|
+
import * as Effect from "effect/Effect"
|
|
14
|
+
import * as Layer from "effect/Layer"
|
|
15
|
+
import * as Predicate from "effect/Predicate"
|
|
16
|
+
import * as Ref from "effect/Ref"
|
|
17
|
+
import * as Schema from "effect/Schema"
|
|
18
|
+
import * as SqlClient from "effect/sql/SqlClient"
|
|
19
|
+
import * as AssistantEvent from "./AssistantEvent.ts"
|
|
20
|
+
import { ChatId } from "./ChatId.ts"
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Service that stores each chat's events as an ordered list of entries.
|
|
24
|
+
*
|
|
25
|
+
* @category services
|
|
26
|
+
* @since 0.1.0
|
|
27
|
+
*/
|
|
28
|
+
export class ChatLog extends Context.Service<
|
|
29
|
+
ChatLog,
|
|
30
|
+
{
|
|
31
|
+
/** Appends events to a chat and returns them as stored entries. */
|
|
32
|
+
readonly append: (
|
|
33
|
+
chatId: ChatId,
|
|
34
|
+
events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>
|
|
35
|
+
) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>
|
|
36
|
+
/** Reads a chat's stored entries in order, either all of them or only those after a `seq`. */
|
|
37
|
+
readonly read: (chatId: ChatId, options?: ReadOptions) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>
|
|
38
|
+
/**
|
|
39
|
+
* Returns the chats whose last entry ends neither a turn nor the chat. After a restart, these
|
|
40
|
+
* are the chats that may still owe a turn.
|
|
41
|
+
*/
|
|
42
|
+
readonly unfinished: Effect.Effect<ReadonlyArray<ChatId>>
|
|
43
|
+
}
|
|
44
|
+
>()("effect-assistant/ChatLog") {}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Represents the options that select which of a chat's entries to read.
|
|
48
|
+
*
|
|
49
|
+
* @category models
|
|
50
|
+
* @since 0.1.0
|
|
51
|
+
*/
|
|
52
|
+
export interface ReadOptions {
|
|
53
|
+
/** Reads only the entries after this `seq`. */
|
|
54
|
+
readonly after?: number | undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Represents the storage that a `ChatLog` is built on.
|
|
59
|
+
*
|
|
60
|
+
* A storage reads a chat's entries, finds a chat's last `seq`, writes new entries, and finds every
|
|
61
|
+
* chat's last entry. Entries are never removed, so a deleted chat keeps its log, which ends in
|
|
62
|
+
* `ChatDeleted`.
|
|
63
|
+
*
|
|
64
|
+
* @category models
|
|
65
|
+
* @since 0.1.0
|
|
66
|
+
*/
|
|
67
|
+
export interface Storage {
|
|
68
|
+
readonly read: (chatId: ChatId, options?: ReadOptions) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>
|
|
69
|
+
readonly lastSeq: (chatId: ChatId) => Effect.Effect<number>
|
|
70
|
+
readonly write: (chatId: ChatId, entries: ReadonlyArray<AssistantEvent.Entry>) => Effect.Effect<void>
|
|
71
|
+
/** Returns the last entry of every chat. */
|
|
72
|
+
readonly lastEntries: Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Creates a `ChatLog` from a {@link Storage}. `append` numbers new entries after the chat's last
|
|
77
|
+
* `seq` and stamps them with the current time.
|
|
78
|
+
*
|
|
79
|
+
* @category constructors
|
|
80
|
+
* @since 0.1.0
|
|
81
|
+
*/
|
|
82
|
+
export const make = (storage: Storage): ChatLog["Service"] =>
|
|
83
|
+
ChatLog.of({
|
|
84
|
+
read: storage.read,
|
|
85
|
+
append: Effect.fn("ChatLog.append")(function* (
|
|
86
|
+
chatId: ChatId,
|
|
87
|
+
events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>
|
|
88
|
+
) {
|
|
89
|
+
if (events.length === 0) return []
|
|
90
|
+
|
|
91
|
+
const seq = yield* storage.lastSeq(chatId)
|
|
92
|
+
const at = yield* Clock.currentTimeMillis
|
|
93
|
+
const entries = events.map((event, n) => AssistantEvent.Entry.make({ chatId, seq: seq + n + 1, at, event }))
|
|
94
|
+
|
|
95
|
+
yield* storage.write(chatId, entries)
|
|
96
|
+
|
|
97
|
+
return entries
|
|
98
|
+
}),
|
|
99
|
+
unfinished: Effect.gen(function* () {
|
|
100
|
+
const last = yield* storage.lastEntries
|
|
101
|
+
|
|
102
|
+
// A deleted chat owes nothing.
|
|
103
|
+
return last.flatMap((entry) =>
|
|
104
|
+
AssistantEvent.isTurnEnd(entry.event) || Predicate.isTagged(entry.event, "ChatDeleted") ? [] : [entry.chatId]
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Layer that keeps every chat in memory. The log is lost when the process stops.
|
|
111
|
+
*
|
|
112
|
+
* @category layers
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
*/
|
|
115
|
+
export const layerMemory: Layer.Layer<ChatLog> = Layer.effect(
|
|
116
|
+
ChatLog,
|
|
117
|
+
Effect.gen(function* () {
|
|
118
|
+
const chats = yield* Ref.make(new Map<ChatId, ReadonlyArray<AssistantEvent.Entry>>())
|
|
119
|
+
|
|
120
|
+
return make({
|
|
121
|
+
read: Effect.fnUntraced(function* (chatId: ChatId, options?: ReadOptions) {
|
|
122
|
+
const all = yield* Ref.get(chats)
|
|
123
|
+
|
|
124
|
+
return (all.get(chatId) ?? []).filter((entry) => entry.seq > (options?.after ?? 0))
|
|
125
|
+
}),
|
|
126
|
+
lastSeq: Effect.fnUntraced(function* (chatId: ChatId) {
|
|
127
|
+
const all = yield* Ref.get(chats)
|
|
128
|
+
|
|
129
|
+
return all.get(chatId)?.at(-1)?.seq ?? 0
|
|
130
|
+
}),
|
|
131
|
+
write: (chatId, entries) =>
|
|
132
|
+
Ref.update(chats, (all) => new Map(all).set(chatId, [...(all.get(chatId) ?? []), ...entries])),
|
|
133
|
+
lastEntries: Effect.gen(function* () {
|
|
134
|
+
const all = yield* Ref.get(chats)
|
|
135
|
+
|
|
136
|
+
return [...all.values()].flatMap((entries) => entries.slice(-1))
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const EventJson = Schema.fromJsonString(Schema.Union([AssistantEvent.AssistantEvent, AssistantEvent.StoredAppEvent]))
|
|
143
|
+
|
|
144
|
+
// Postgres returns BIGINT and DOUBLE PRECISION columns as strings; other databases as numbers.
|
|
145
|
+
const SqlNumber = Schema.Union([Schema.Finite, Schema.FiniteFromString])
|
|
146
|
+
|
|
147
|
+
const Row = Schema.Struct({ chat_id: ChatId, seq: SqlNumber, at: SqlNumber, event: EventJson })
|
|
148
|
+
|
|
149
|
+
const decodeLastSeq = Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ seq: Schema.NullOr(SqlNumber) })))
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Layer that stores chats in a SQL table named `chat_log`.
|
|
153
|
+
*
|
|
154
|
+
* **Details**
|
|
155
|
+
*
|
|
156
|
+
* The table must already exist. It has the columns `chat_id`, `seq`, `at`, and `event`, with the
|
|
157
|
+
* primary key `(chat_id, seq)`. The layer requires a `SqlClient`.
|
|
158
|
+
*
|
|
159
|
+
* **Example** (Storing chats in Postgres)
|
|
160
|
+
*
|
|
161
|
+
* ```ts
|
|
162
|
+
* import * as PgClient from "@effect/sql-pg/PgClient"
|
|
163
|
+
* import { ChatLog } from "effect-assistant"
|
|
164
|
+
* import * as Config from "effect/Config"
|
|
165
|
+
* import * as Layer from "effect/Layer"
|
|
166
|
+
*
|
|
167
|
+
* const ChatLogLive = ChatLog.layerSql.pipe(
|
|
168
|
+
* Layer.provide(PgClient.layerConfig({ url: Config.Redacted("DATABASE_URL") }))
|
|
169
|
+
* )
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* The table, in Postgres:
|
|
173
|
+
*
|
|
174
|
+
* ```sql
|
|
175
|
+
* CREATE TABLE chat_log (
|
|
176
|
+
* chat_id TEXT NOT NULL,
|
|
177
|
+
* seq INTEGER NOT NULL,
|
|
178
|
+
* at DOUBLE PRECISION NOT NULL,
|
|
179
|
+
* event TEXT NOT NULL,
|
|
180
|
+
* PRIMARY KEY (chat_id, seq)
|
|
181
|
+
* );
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
184
|
+
* @category layers
|
|
185
|
+
* @since 0.1.0
|
|
186
|
+
*/
|
|
187
|
+
export const layerSql: Layer.Layer<ChatLog, never, SqlClient.SqlClient> = Layer.effect(
|
|
188
|
+
ChatLog,
|
|
189
|
+
Effect.gen(function* () {
|
|
190
|
+
const sql = (yield* SqlClient.SqlClient).withoutTransforms()
|
|
191
|
+
|
|
192
|
+
const toEntries = Effect.fnUntraced(function* (rows: ReadonlyArray<unknown>) {
|
|
193
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Row))(rows)
|
|
194
|
+
|
|
195
|
+
return decoded.map((row) =>
|
|
196
|
+
AssistantEvent.Entry.make({
|
|
197
|
+
chatId: row.chat_id,
|
|
198
|
+
seq: row.seq,
|
|
199
|
+
at: row.at,
|
|
200
|
+
event: row.event
|
|
201
|
+
})
|
|
202
|
+
)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
const encodeEvent = Schema.encodeEffect(EventJson)
|
|
206
|
+
|
|
207
|
+
return make({
|
|
208
|
+
read: Effect.fnUntraced(function* (chatId: ChatId, options?: ReadOptions) {
|
|
209
|
+
const rows = yield* sql`
|
|
210
|
+
SELECT chat_id, seq, at, event FROM chat_log WHERE chat_id = ${chatId} AND seq > ${options?.after ?? 0} ORDER BY seq
|
|
211
|
+
`
|
|
212
|
+
|
|
213
|
+
return yield* toEntries(rows)
|
|
214
|
+
}, Effect.orDie),
|
|
215
|
+
lastSeq: Effect.fnUntraced(function* (chatId: ChatId) {
|
|
216
|
+
const result = yield* sql`
|
|
217
|
+
SELECT MAX(seq) AS seq FROM chat_log WHERE chat_id = ${chatId}
|
|
218
|
+
`
|
|
219
|
+
|
|
220
|
+
const rows = yield* decodeLastSeq(result)
|
|
221
|
+
|
|
222
|
+
return rows[0]?.seq ?? 0
|
|
223
|
+
}, Effect.orDie),
|
|
224
|
+
write: Effect.fnUntraced(function* (chatId: ChatId, entries: ReadonlyArray<AssistantEvent.Entry>) {
|
|
225
|
+
const rows = yield* Effect.forEach(
|
|
226
|
+
entries,
|
|
227
|
+
Effect.fnUntraced(function* (entry: AssistantEvent.Entry) {
|
|
228
|
+
const event = yield* encodeEvent(entry.event)
|
|
229
|
+
|
|
230
|
+
return { chat_id: chatId, seq: entry.seq, at: entry.at, event }
|
|
231
|
+
})
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
yield* sql`INSERT INTO chat_log ${sql.insert(rows)}`
|
|
235
|
+
}, Effect.orDie),
|
|
236
|
+
lastEntries: Effect.gen(function* () {
|
|
237
|
+
const rows = yield* sql`
|
|
238
|
+
SELECT e.chat_id, e.seq, e.at, e.event FROM chat_log e
|
|
239
|
+
JOIN (SELECT chat_id, MAX(seq) AS seq FROM chat_log GROUP BY chat_id) last
|
|
240
|
+
ON last.chat_id = e.chat_id AND last.seq = e.seq
|
|
241
|
+
`
|
|
242
|
+
|
|
243
|
+
return yield* toEntries(rows)
|
|
244
|
+
}).pipe(Effect.orDie)
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
)
|