@economic/agents 2.0.1 → 2.1.1
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/index.mjs +5 -5
- package/dist/{telemetry-a-1XBTxr.mjs → telemetry-BKgBMVF0.mjs} +25 -25
- package/dist/v2.d.mts +21 -18
- package/dist/v2.mjs +139 -124
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-
|
|
1
|
+
import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-BKgBMVF0.mjs";
|
|
2
2
|
import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
|
|
3
3
|
import { Agent as Agent$1, callable, getCurrentAgent, routeAgentRequest as routeAgentRequest$1 } from "agents";
|
|
4
4
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
|
@@ -359,7 +359,7 @@ var Agent = class extends Agent$1 {
|
|
|
359
359
|
isEnabled: true,
|
|
360
360
|
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
361
361
|
agentName: this.constructor.name,
|
|
362
|
-
|
|
362
|
+
chatId: this.name,
|
|
363
363
|
userId: this.getUserId(),
|
|
364
364
|
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
365
365
|
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
|
|
@@ -367,7 +367,7 @@ var Agent = class extends Agent$1 {
|
|
|
367
367
|
metadata: {
|
|
368
368
|
agentName: this.constructor.name,
|
|
369
369
|
version: "v1",
|
|
370
|
-
|
|
370
|
+
chatId: this.name,
|
|
371
371
|
userId: this.getUserId(),
|
|
372
372
|
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
373
373
|
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
|
|
@@ -769,7 +769,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
769
769
|
isEnabled: true,
|
|
770
770
|
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
771
771
|
agentName: this.constructor.name,
|
|
772
|
-
|
|
772
|
+
chatId: this.name,
|
|
773
773
|
userId: this.getUserId(),
|
|
774
774
|
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
775
775
|
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
|
|
@@ -777,7 +777,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
777
777
|
metadata: {
|
|
778
778
|
agentName: this.constructor.name,
|
|
779
779
|
version: "v1",
|
|
780
|
-
|
|
780
|
+
chatId: this.name,
|
|
781
781
|
userId: this.getUserId(),
|
|
782
782
|
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
783
783
|
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
|
|
@@ -125,12 +125,12 @@ function parseJson(value) {
|
|
|
125
125
|
function safePathSegment(value, fallback) {
|
|
126
126
|
return (value || fallback).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
127
127
|
}
|
|
128
|
-
function createAuditLogKey(agentName,
|
|
128
|
+
function createAuditLogKey(agentName, chatId) {
|
|
129
129
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
130
130
|
const id = crypto.randomUUID().slice(0, 8);
|
|
131
131
|
return [
|
|
132
132
|
safePathSegment(agentName, "unknown-agent"),
|
|
133
|
-
safePathSegment(
|
|
133
|
+
safePathSegment(chatId, "unknown-chat"),
|
|
134
134
|
`${timestamp}-${id}.json`
|
|
135
135
|
].join("/");
|
|
136
136
|
}
|
|
@@ -186,15 +186,15 @@ function pushToolCall(map, key, toolCall) {
|
|
|
186
186
|
else map.set(key, [toolCall]);
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
|
-
* Stores the last ai.streamText.doStream span per
|
|
190
|
-
* Its ai.prompt.messages contains the full
|
|
189
|
+
* Stores the last ai.streamText.doStream span per chat.
|
|
190
|
+
* Its ai.prompt.messages contains the full chat history including all
|
|
191
191
|
* intermediate tool_use + tool_result turns from the agentic loop.
|
|
192
192
|
*/
|
|
193
|
-
const
|
|
193
|
+
const lastDoStreamByChat = /* @__PURE__ */ new Map();
|
|
194
194
|
const toolCallsByParentSpan = /* @__PURE__ */ new Map();
|
|
195
|
-
const
|
|
195
|
+
const toolCallsByChat = /* @__PURE__ */ new Map();
|
|
196
196
|
const pendingToolCalls = [];
|
|
197
|
-
const
|
|
197
|
+
const currentSkillByChat = /* @__PURE__ */ new Map();
|
|
198
198
|
function rememberToolCall(span) {
|
|
199
199
|
const parentSpanId = span.parentSpanContext?.spanId;
|
|
200
200
|
const toolName = stringAttribute(span, "ai.toolCall.name");
|
|
@@ -208,34 +208,34 @@ function rememberToolCall(span) {
|
|
|
208
208
|
if (parentSpanId) pushToolCall(toolCallsByParentSpan, parentSpanId, toolCall);
|
|
209
209
|
pendingToolCalls.push(toolCall);
|
|
210
210
|
}
|
|
211
|
-
function
|
|
211
|
+
function attachToolCallsToChat(span, chatId) {
|
|
212
212
|
const spanId = span.spanContext().spanId;
|
|
213
213
|
const toolCalls = toolCallsByParentSpan.get(spanId) ?? pendingToolCalls.splice(0);
|
|
214
214
|
if (!toolCalls.length) return;
|
|
215
215
|
toolCallsByParentSpan.delete(spanId);
|
|
216
|
-
const currentSkill =
|
|
216
|
+
const currentSkill = currentSkillByChat.get(chatId);
|
|
217
217
|
const attributedToolCalls = toolCalls.map((toolCall) => {
|
|
218
218
|
const skillName = toolCall.skillName ?? currentSkill;
|
|
219
|
-
if (toolCall.skillName)
|
|
219
|
+
if (toolCall.skillName) currentSkillByChat.set(chatId, toolCall.skillName);
|
|
220
220
|
return {
|
|
221
221
|
...toolCall,
|
|
222
222
|
...skillName ? { skillName } : {}
|
|
223
223
|
};
|
|
224
224
|
});
|
|
225
|
-
|
|
225
|
+
toolCallsByChat.set(chatId, [...toolCallsByChat.get(chatId) ?? [], ...attributedToolCalls]);
|
|
226
226
|
}
|
|
227
227
|
function buildAuditLog(span, context) {
|
|
228
|
-
const lastDoStream =
|
|
229
|
-
|
|
228
|
+
const lastDoStream = lastDoStreamByChat.get(context.chatId);
|
|
229
|
+
lastDoStreamByChat.delete(context.chatId);
|
|
230
230
|
const promptMessages = parseJson(stringAttribute(lastDoStream ?? span, "ai.prompt.messages"));
|
|
231
231
|
const fallbackPrompt = parseJson(stringAttribute(span, "ai.prompt"));
|
|
232
232
|
const inputMessages = promptMessages ?? fallbackPrompt?.messages ?? [];
|
|
233
|
-
const spanToolCalls =
|
|
234
|
-
|
|
233
|
+
const spanToolCalls = toolCallsByChat.get(context.chatId) ?? [];
|
|
234
|
+
toolCallsByChat.delete(context.chatId);
|
|
235
235
|
return {
|
|
236
|
-
id: createAuditLogKey(context.agentName, context.
|
|
236
|
+
id: createAuditLogKey(context.agentName, context.chatId),
|
|
237
237
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
238
|
-
|
|
238
|
+
chatId: context.chatId,
|
|
239
239
|
agent: {
|
|
240
240
|
name: context.agentName,
|
|
241
241
|
llm: {
|
|
@@ -256,8 +256,8 @@ function buildAuditLog(span, context) {
|
|
|
256
256
|
tools: [...extractTools(inputMessages), ...spanToolCalls]
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
|
-
function rememberDoStream(span,
|
|
260
|
-
|
|
259
|
+
function rememberDoStream(span, chatId) {
|
|
260
|
+
lastDoStreamByChat.set(chatId, span);
|
|
261
261
|
}
|
|
262
262
|
async function handleAuditSpan(span, auditLogs, context) {
|
|
263
263
|
const auditLog = buildAuditLog(span, context);
|
|
@@ -284,7 +284,7 @@ function handleAnalyticsSpan(span, analytics) {
|
|
|
284
284
|
blobs: [
|
|
285
285
|
"llm_call",
|
|
286
286
|
stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
|
|
287
|
-
stringAttribute(span, "ai.telemetry.metadata.
|
|
287
|
+
stringAttribute(span, "ai.telemetry.metadata.chatId") ?? "",
|
|
288
288
|
stringAttribute(span, "ai.model.id") ?? "",
|
|
289
289
|
stringAttribute(span, "ai.model.provider") ?? "",
|
|
290
290
|
stringAttribute(span, "ai.response.finishReason") ?? ""
|
|
@@ -305,15 +305,15 @@ function handleAnalyticsSpan(span, analytics) {
|
|
|
305
305
|
if (span.name === "ai.toolCall") {
|
|
306
306
|
const toolName = stringAttribute(span, "ai.toolCall.name");
|
|
307
307
|
const toolInput = parseJson(stringAttribute(span, "ai.toolCall.args"));
|
|
308
|
-
const
|
|
309
|
-
const skillName = extractSkillName(toolName, toolInput) ??
|
|
308
|
+
const chatId = stringAttribute(span, "ai.telemetry.metadata.chatId") ?? "";
|
|
309
|
+
const skillName = extractSkillName(toolName, toolInput) ?? currentSkillByChat.get(chatId) ?? "";
|
|
310
310
|
const success = span.status.code === 0;
|
|
311
311
|
writeAnalyticsDatapoint(analytics, {
|
|
312
312
|
indexes: [stringAttribute(span, "ai.telemetry.metadata.userId") ?? ""],
|
|
313
313
|
blobs: [
|
|
314
314
|
"tool_call",
|
|
315
315
|
stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
|
|
316
|
-
|
|
316
|
+
chatId,
|
|
317
317
|
toolName ?? "",
|
|
318
318
|
skillName,
|
|
319
319
|
success ? "success" : "error"
|
|
@@ -346,8 +346,8 @@ var AgentSpanExporter = class {
|
|
|
346
346
|
(async () => {
|
|
347
347
|
try {
|
|
348
348
|
for (const span of spans) if (span.name === "ai.streamText.doStream") {
|
|
349
|
-
rememberDoStream(span, this.context.
|
|
350
|
-
|
|
349
|
+
rememberDoStream(span, this.context.chatId);
|
|
350
|
+
attachToolCallsToChat(span, this.context.chatId);
|
|
351
351
|
handleAnalyticsSpan(span, this.analytics);
|
|
352
352
|
} else if (span.name === "ai.streamText") await handleAuditSpan(span, this.auditLogs, this.context);
|
|
353
353
|
else if (span.name === "ai.toolCall") {
|
package/dist/v2.d.mts
CHANGED
|
@@ -19,6 +19,13 @@ interface AgentEnv {
|
|
|
19
19
|
AGENTS_ANALYTICS: AnalyticsEngineDataset;
|
|
20
20
|
SKILLS_BUCKET: R2Bucket;
|
|
21
21
|
}
|
|
22
|
+
type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized";
|
|
23
|
+
type AgentConnectionType = "agent" | "chat" | "assistant";
|
|
24
|
+
type AgentConnectionState = {
|
|
25
|
+
status: AgentConnectionStatus;
|
|
26
|
+
type: AgentConnectionType; /** Set by `Assistant` so clients can derive the sub-agent routing name from state. */
|
|
27
|
+
subAgentName?: string;
|
|
28
|
+
};
|
|
22
29
|
//#endregion
|
|
23
30
|
//#region src/server/v2/util/tools.d.ts
|
|
24
31
|
type ToolSet = Record<string, Tool>;
|
|
@@ -39,12 +46,6 @@ declare function skill<Context extends Record<string, unknown> = Record<string,
|
|
|
39
46
|
//#endregion
|
|
40
47
|
//#region src/server/v2/agents/Agent.d.ts
|
|
41
48
|
declare function getCurrentToolContext(): any;
|
|
42
|
-
type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized";
|
|
43
|
-
type AgentConnectionType = "agent" | "chat" | "assistant";
|
|
44
|
-
type AgentConnectionState = {
|
|
45
|
-
status: AgentConnectionStatus;
|
|
46
|
-
type: AgentConnectionType;
|
|
47
|
-
};
|
|
48
49
|
declare abstract class Agent<RequestContext extends Record<string, unknown> = Record<string, unknown>, UserContext extends Record<string, unknown> = Record<string, unknown>> extends Think<Cloudflare.Env & AgentEnv, AgentConnectionState> {
|
|
49
50
|
initialState: AgentConnectionState;
|
|
50
51
|
protected clientIp?: string;
|
|
@@ -58,6 +59,7 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
58
59
|
abstract getSystemPrompt(ctx?: ToolContext<RequestContext, UserContext>): string;
|
|
59
60
|
configureSession(session: Session): Session;
|
|
60
61
|
onStart(): Promise<void>;
|
|
62
|
+
onClose(): Promise<void>;
|
|
61
63
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
62
64
|
/**
|
|
63
65
|
* Merges the client request `body` into `experimental_context` for tools
|
|
@@ -130,21 +132,21 @@ declare abstract class ChatAgent<RequestContext extends Record<string, unknown>
|
|
|
130
132
|
*/
|
|
131
133
|
rateMessage(messageId: string, rating: number, comment?: string): Promise<void>;
|
|
132
134
|
/**
|
|
133
|
-
* Returns all message ratings for the current
|
|
134
|
-
* @returns All message ratings for the current
|
|
135
|
+
* Returns all message ratings for the current chat.
|
|
136
|
+
* @returns All message ratings for the current chat.
|
|
135
137
|
*/
|
|
136
138
|
getMessageRatings(): Promise<any>;
|
|
137
139
|
}
|
|
138
140
|
//#endregion
|
|
139
|
-
//#region src/server/v2/features/
|
|
140
|
-
type
|
|
141
|
+
//#region src/server/v2/features/chats.d.ts
|
|
142
|
+
type Chat = {
|
|
141
143
|
durable_object_name: string;
|
|
142
144
|
title?: string;
|
|
143
145
|
summary?: string;
|
|
144
146
|
created_at: number;
|
|
145
147
|
updated_at: number;
|
|
146
148
|
};
|
|
147
|
-
declare const
|
|
149
|
+
declare const DELETE_CHAT_CALLBACK: "deleteChatCallback";
|
|
148
150
|
//#endregion
|
|
149
151
|
//#region src/server/v2/agents/Assistant.d.ts
|
|
150
152
|
declare abstract class Assistant extends Agent$1<Cloudflare.Env, AgentConnectionState> {
|
|
@@ -152,13 +154,14 @@ declare abstract class Assistant extends Agent$1<Cloudflare.Env, AgentConnection
|
|
|
152
154
|
protected abstract agent: SubAgentClass<ChatAgent>;
|
|
153
155
|
protected abstract fastModel: LanguageModel;
|
|
154
156
|
onStart(): void;
|
|
157
|
+
onClose(): Promise<void>;
|
|
155
158
|
onConnect(): Promise<void>;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
private [
|
|
161
|
-
private
|
|
159
|
+
createChat(): Promise<string>;
|
|
160
|
+
deleteChat(id: string): Promise<void>;
|
|
161
|
+
getChats(): Promise<Chat[]>;
|
|
162
|
+
recordChatTurn(durableObjectName: string, messages: UIMessage[]): Promise<void>;
|
|
163
|
+
private [DELETE_CHAT_CALLBACK];
|
|
164
|
+
private scheduleChatForAutoDeletion;
|
|
162
165
|
}
|
|
163
166
|
//#endregion
|
|
164
|
-
export { Agent, type AgentEnv, Assistant, ChatAgent, type Skill, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, skill, tool };
|
|
167
|
+
export { Agent, type AgentConnectionState, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type Skill, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, skill, tool };
|
package/dist/v2.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-
|
|
1
|
+
import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-BKgBMVF0.mjs";
|
|
2
2
|
import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
|
|
3
3
|
import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
|
|
4
4
|
import { Think } from "@cloudflare/think";
|
|
5
5
|
import { R2SkillProvider } from "agents/experimental/memory/session";
|
|
6
|
-
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
7
6
|
import { nanoid } from "nanoid";
|
|
7
|
+
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
8
8
|
//#region src/server/v2/util/tools.ts
|
|
9
9
|
function tool(tool) {
|
|
10
10
|
return tool$1(tool);
|
|
@@ -106,6 +106,12 @@ var Agent = class extends Think {
|
|
|
106
106
|
throw new Error("Could not connect to agent, bindings not found");
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
async onClose() {
|
|
110
|
+
this.setState({
|
|
111
|
+
...this.initialState,
|
|
112
|
+
status: "disconnected"
|
|
113
|
+
});
|
|
114
|
+
}
|
|
109
115
|
async onConnect(connection, ctx) {
|
|
110
116
|
this.clientIp = ctx.request.headers.get("CF-Connecting-IP") ?? ctx.request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim();
|
|
111
117
|
this.forwardedFor = ctx.request.headers.get("X-Forwarded-For") ?? void 0;
|
|
@@ -179,7 +185,7 @@ var Agent = class extends Think {
|
|
|
179
185
|
isEnabled: true,
|
|
180
186
|
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
181
187
|
agentName: this.constructor.name,
|
|
182
|
-
|
|
188
|
+
chatId: this.name,
|
|
183
189
|
userId: this.getUserIdFromDurableObjectName(),
|
|
184
190
|
clientIp: this.clientIp,
|
|
185
191
|
forwardedFor: this.forwardedFor
|
|
@@ -187,7 +193,7 @@ var Agent = class extends Think {
|
|
|
187
193
|
metadata: {
|
|
188
194
|
agentName: this.constructor.name,
|
|
189
195
|
version: "v2",
|
|
190
|
-
|
|
196
|
+
chatId: this.name,
|
|
191
197
|
userId: this.getUserIdFromDurableObjectName()
|
|
192
198
|
}
|
|
193
199
|
}
|
|
@@ -271,77 +277,14 @@ var Agent = class extends Think {
|
|
|
271
277
|
_userContext;
|
|
272
278
|
};
|
|
273
279
|
//#endregion
|
|
274
|
-
//#region src/server/v2/features/
|
|
275
|
-
const COMPACTION_TOKEN_THRESHOLD = 1e5;
|
|
276
|
-
const createCompactFn = (model) => createCompactFunction({ summarize: (prompt) => generateText({
|
|
277
|
-
model,
|
|
278
|
-
prompt
|
|
279
|
-
}).then((r) => r.text) });
|
|
280
|
-
/**
|
|
281
|
-
* Ensures that the ratings table exists.
|
|
282
|
-
* @param sql - The SQL function to use to execute the query.
|
|
283
|
-
*/
|
|
284
|
-
function ensureRatingsTableExists(sql) {
|
|
285
|
-
try {
|
|
286
|
-
sql`CREATE TABLE IF NOT EXISTS assistant_messages_ratings (
|
|
287
|
-
message_id TEXT NOT NULL,
|
|
288
|
-
durable_object_name TEXT NOT NULL,
|
|
289
|
-
rating INTEGER,
|
|
290
|
-
comment TEXT,
|
|
291
|
-
created_at TEXT NOT NULL,
|
|
292
|
-
updated_at TEXT NOT NULL,
|
|
293
|
-
PRIMARY KEY (message_id, durable_object_name)
|
|
294
|
-
)`;
|
|
295
|
-
} catch (error) {
|
|
296
|
-
console.error("[Agent] Failed to create ratings table", error);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Rates a message.
|
|
301
|
-
* @param sql - The SQL function to use to execute the query.
|
|
302
|
-
* @param messageId - The ID of the message to rate.
|
|
303
|
-
* @param durable_object_name - The name of the Durable Object to rate the message for.
|
|
304
|
-
* @param rating - The rating to give the message.
|
|
305
|
-
* @param now - The date and time to use for the created_at and updated_at columns.
|
|
306
|
-
*/
|
|
307
|
-
function rateMessage(sql, messageId, durable_object_name, rating, comment, now = /* @__PURE__ */ new Date()) {
|
|
308
|
-
try {
|
|
309
|
-
sql`INSERT INTO assistant_messages_ratings (message_id, durable_object_name, rating, comment, created_at, updated_at)
|
|
310
|
-
VALUES (${messageId}, ${durable_object_name}, ${rating}, ${comment ?? null}, ${now.toISOString()}, ${now.toISOString()})
|
|
311
|
-
ON CONFLICT (message_id, durable_object_name) DO UPDATE SET
|
|
312
|
-
rating = excluded.rating,
|
|
313
|
-
comment = excluded.comment,
|
|
314
|
-
updated_at = excluded.updated_at`;
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error("[Agent] Failed to rate message", error);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Gets the ratings for a message.
|
|
321
|
-
* @param sql - The SQL function to use to execute the query.
|
|
322
|
-
* @param durable_object_name - The name of the Durable Object to get the ratings for.
|
|
323
|
-
* @returns A record of message IDs and their ratings.
|
|
324
|
-
*/
|
|
325
|
-
function getMessageRatings(sql, durable_object_name) {
|
|
326
|
-
try {
|
|
327
|
-
const ratings = sql`SELECT message_id, rating, comment FROM assistant_messages_ratings WHERE durable_object_name = ${durable_object_name}`;
|
|
328
|
-
return Object.fromEntries(ratings.map((row) => [row.message_id, {
|
|
329
|
-
rating: row.rating,
|
|
330
|
-
comment: row.comment
|
|
331
|
-
}]));
|
|
332
|
-
} catch {
|
|
333
|
-
return {};
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
//#endregion
|
|
337
|
-
//#region src/server/v2/features/conversations.ts
|
|
280
|
+
//#region src/server/v2/features/chats.ts
|
|
338
281
|
/**
|
|
339
|
-
* Ensures that the
|
|
282
|
+
* Ensures that the chats table exists.
|
|
340
283
|
* @param sql - The SQL function to use to execute the query.
|
|
341
284
|
*/
|
|
342
|
-
function
|
|
285
|
+
function ensureChatsTableExists(sql) {
|
|
343
286
|
try {
|
|
344
|
-
sql`CREATE TABLE IF NOT EXISTS
|
|
287
|
+
sql`CREATE TABLE IF NOT EXISTS chats (
|
|
345
288
|
durable_object_name TEXT NOT NULL,
|
|
346
289
|
title TEXT,
|
|
347
290
|
summary TEXT,
|
|
@@ -350,80 +293,80 @@ function ensureConversationsTableExists(sql) {
|
|
|
350
293
|
PRIMARY KEY (durable_object_name)
|
|
351
294
|
)`;
|
|
352
295
|
} catch (error) {
|
|
353
|
-
console.error("[Agent] Failed to create
|
|
296
|
+
console.error("[Agent] Failed to create chats table", error);
|
|
354
297
|
}
|
|
355
298
|
}
|
|
356
|
-
function
|
|
357
|
-
sql`INSERT INTO
|
|
299
|
+
function registerChat(sql, durableObjectName, dateTime) {
|
|
300
|
+
sql`INSERT INTO chats (durable_object_name, created_at, updated_at)
|
|
358
301
|
VALUES (${durableObjectName}, ${dateTime}, ${dateTime})`;
|
|
359
302
|
}
|
|
360
|
-
function
|
|
361
|
-
sql`DELETE FROM
|
|
303
|
+
function deleteChat(sql, durableObjectName) {
|
|
304
|
+
sql`DELETE FROM chats WHERE durable_object_name = ${durableObjectName}`;
|
|
362
305
|
}
|
|
363
|
-
function
|
|
364
|
-
return sql`SELECT * FROM
|
|
306
|
+
function getChat(sql, durableObjectName) {
|
|
307
|
+
return sql`SELECT * FROM chats WHERE durable_object_name = ${durableObjectName}`[0] ?? null;
|
|
365
308
|
}
|
|
366
|
-
async function
|
|
367
|
-
return sql`SELECT * FROM
|
|
309
|
+
async function getChats(sql) {
|
|
310
|
+
return sql`SELECT * FROM chats ORDER BY updated_at DESC`;
|
|
368
311
|
}
|
|
369
|
-
function
|
|
370
|
-
return schedules.filter((schedule) => schedule.callback ===
|
|
312
|
+
function getDeleteChatScheduleIds(schedules) {
|
|
313
|
+
return schedules.filter((schedule) => schedule.callback === DELETE_CHAT_CALLBACK).map((schedule) => schedule.id);
|
|
371
314
|
}
|
|
372
315
|
/**
|
|
373
316
|
* Number of recent messages passed to `generateSummary` for rolling
|
|
374
317
|
* summarization. Keeping this bounded prevents the prompt growing
|
|
375
|
-
* unboundedly regardless of
|
|
318
|
+
* unboundedly regardless of chat length.
|
|
376
319
|
*/
|
|
377
|
-
const
|
|
378
|
-
async function
|
|
379
|
-
const
|
|
380
|
-
if (!(!
|
|
320
|
+
const CHAT_RECENT_MESSAGES_COUNT = 20;
|
|
321
|
+
async function summariseChatWithAI(sql, durableObjectName, messages, model) {
|
|
322
|
+
const chat = getChat(sql, durableObjectName);
|
|
323
|
+
if (!(!chat || !chat.title || messages.length % CHAT_RECENT_MESSAGES_COUNT === 0)) return;
|
|
381
324
|
let systemPrompt = `
|
|
382
|
-
You are a helpful assistant that summarises
|
|
383
|
-
You will be given a list of messages and you need to generate a title and summary for the
|
|
384
|
-
The title should be a short title for the
|
|
385
|
-
The summary should be a short 1-2 sentence summary of the
|
|
386
|
-
The summary should reflect the direction of the
|
|
387
|
-
if (
|
|
325
|
+
You are a helpful assistant that summarises chats.
|
|
326
|
+
You will be given a list of messages and you need to generate a title and summary for the chat.
|
|
327
|
+
The title should be a short title for the chat, max 8-10 words.
|
|
328
|
+
The summary should be a short 1-2 sentence summary of the chat.
|
|
329
|
+
The summary should reflect the direction of the chat.`;
|
|
330
|
+
if (chat) systemPrompt += `${systemPrompt}\n\nThe previous summary: ${chat.summary}`;
|
|
388
331
|
try {
|
|
389
332
|
const { output: { title, summary } } = await generateText({
|
|
390
333
|
model,
|
|
391
334
|
system: systemPrompt,
|
|
392
|
-
messages: await convertToModelMessages(messages.slice(-
|
|
335
|
+
messages: await convertToModelMessages(messages.slice(-CHAT_RECENT_MESSAGES_COUNT)),
|
|
393
336
|
output: Output.object({ schema: jsonSchema({
|
|
394
337
|
type: "object",
|
|
395
338
|
properties: {
|
|
396
339
|
title: {
|
|
397
340
|
type: "string",
|
|
398
|
-
description: "Short title for the
|
|
341
|
+
description: "Short title for the chat, max 8-10 words"
|
|
399
342
|
},
|
|
400
343
|
summary: {
|
|
401
344
|
type: "string",
|
|
402
|
-
description: "A short 1-2 sentence summary of the
|
|
345
|
+
description: "A short 1-2 sentence summary of the chat. If the chat direction has changed from the previous summary, reflect the new direction."
|
|
403
346
|
}
|
|
404
347
|
},
|
|
405
348
|
required: ["title", "summary"]
|
|
406
349
|
}) })
|
|
407
350
|
});
|
|
408
351
|
if (!title || !summary) {
|
|
409
|
-
console.error("[Assistant] Failed to generate
|
|
352
|
+
console.error("[Assistant] Failed to generate chat title and summary", { durableObjectName });
|
|
410
353
|
return;
|
|
411
354
|
}
|
|
412
|
-
sql`UPDATE
|
|
355
|
+
sql`UPDATE chats
|
|
413
356
|
SET title = ${title},
|
|
414
357
|
summary = ${summary},
|
|
415
358
|
updated_at = ${Date.now()}
|
|
416
359
|
WHERE durable_object_name = ${durableObjectName}`;
|
|
417
|
-
console.info("[Assistant] Generated
|
|
360
|
+
console.info("[Assistant] Generated chat summary", { durableObjectName });
|
|
418
361
|
} catch (error) {
|
|
419
|
-
console.error("[Assistant] Failed to generate
|
|
362
|
+
console.error("[Assistant] Failed to generate chat title and summary", {
|
|
420
363
|
durableObjectName,
|
|
421
364
|
error
|
|
422
365
|
});
|
|
423
366
|
}
|
|
424
367
|
}
|
|
425
|
-
const
|
|
426
|
-
function
|
|
368
|
+
const DELETE_CHAT_CALLBACK = "deleteChatCallback";
|
|
369
|
+
function getChatRetentionMs(days) {
|
|
427
370
|
if (typeof days !== "number" || !Number.isFinite(days) || days <= 0) return null;
|
|
428
371
|
return Math.floor(days * 24 * 60 * 60 * 1e3);
|
|
429
372
|
}
|
|
@@ -437,46 +380,118 @@ var Assistant = class extends Agent$1 {
|
|
|
437
380
|
onStart() {
|
|
438
381
|
this.setState({
|
|
439
382
|
type: "assistant",
|
|
440
|
-
status: "connecting"
|
|
383
|
+
status: "connecting",
|
|
384
|
+
subAgentName: this.agent.name
|
|
385
|
+
});
|
|
386
|
+
ensureChatsTableExists(this.sql.bind(this));
|
|
387
|
+
}
|
|
388
|
+
async onClose() {
|
|
389
|
+
this.setState({
|
|
390
|
+
type: "assistant",
|
|
391
|
+
status: "disconnected",
|
|
392
|
+
subAgentName: this.agent.name
|
|
441
393
|
});
|
|
442
|
-
ensureConversationsTableExists(this.sql.bind(this));
|
|
443
394
|
}
|
|
444
395
|
async onConnect() {
|
|
445
396
|
this.setState({
|
|
446
397
|
type: "assistant",
|
|
447
|
-
status: "connected"
|
|
398
|
+
status: "connected",
|
|
399
|
+
subAgentName: this.agent.name
|
|
448
400
|
});
|
|
449
401
|
}
|
|
450
|
-
@callable() async
|
|
402
|
+
@callable() async createChat() {
|
|
451
403
|
const id = nanoid();
|
|
452
404
|
const now = Date.now();
|
|
453
405
|
await this.subAgent(this.agent, id);
|
|
454
|
-
|
|
406
|
+
registerChat(this.sql.bind(this), id, now);
|
|
455
407
|
return id;
|
|
456
408
|
}
|
|
457
|
-
@callable() async
|
|
409
|
+
@callable() async deleteChat(id) {
|
|
458
410
|
await this.deleteSubAgent(this.agent, id);
|
|
459
|
-
|
|
411
|
+
deleteChat(this.sql.bind(this), id);
|
|
460
412
|
}
|
|
461
|
-
@callable() async
|
|
462
|
-
return
|
|
413
|
+
@callable() async getChats() {
|
|
414
|
+
return getChats(this.sql.bind(this));
|
|
463
415
|
}
|
|
464
|
-
async
|
|
465
|
-
|
|
466
|
-
this.
|
|
416
|
+
async recordChatTurn(durableObjectName, messages) {
|
|
417
|
+
summariseChatWithAI(this.sql.bind(this), durableObjectName, messages, this.fastModel);
|
|
418
|
+
this.scheduleChatForAutoDeletion(durableObjectName);
|
|
467
419
|
}
|
|
468
|
-
async [
|
|
469
|
-
await this.
|
|
420
|
+
async [DELETE_CHAT_CALLBACK](durableObjectName) {
|
|
421
|
+
await this.deleteChat(durableObjectName);
|
|
470
422
|
}
|
|
471
|
-
async
|
|
472
|
-
const retentionMs =
|
|
423
|
+
async scheduleChatForAutoDeletion(durableObjectName) {
|
|
424
|
+
const retentionMs = getChatRetentionMs(90);
|
|
473
425
|
if (retentionMs === null) return;
|
|
474
|
-
const scheduleIds =
|
|
426
|
+
const scheduleIds = getDeleteChatScheduleIds(await this.listSchedules());
|
|
475
427
|
await Promise.all(scheduleIds.map((scheduleId) => this.cancelSchedule(scheduleId)));
|
|
476
|
-
await this.schedule(new Date(Date.now() + retentionMs),
|
|
428
|
+
await this.schedule(new Date(Date.now() + retentionMs), DELETE_CHAT_CALLBACK, durableObjectName, { idempotent: true });
|
|
477
429
|
}
|
|
478
430
|
};
|
|
479
431
|
//#endregion
|
|
432
|
+
//#region src/server/v2/features/messages.ts
|
|
433
|
+
const COMPACTION_TOKEN_THRESHOLD = 1e5;
|
|
434
|
+
const createCompactFn = (model) => createCompactFunction({ summarize: (prompt) => generateText({
|
|
435
|
+
model,
|
|
436
|
+
prompt
|
|
437
|
+
}).then((r) => r.text) });
|
|
438
|
+
/**
|
|
439
|
+
* Ensures that the ratings table exists.
|
|
440
|
+
* @param sql - The SQL function to use to execute the query.
|
|
441
|
+
*/
|
|
442
|
+
function ensureRatingsTableExists(sql) {
|
|
443
|
+
try {
|
|
444
|
+
sql`CREATE TABLE IF NOT EXISTS assistant_messages_ratings (
|
|
445
|
+
message_id TEXT NOT NULL,
|
|
446
|
+
durable_object_name TEXT NOT NULL,
|
|
447
|
+
rating INTEGER,
|
|
448
|
+
comment TEXT,
|
|
449
|
+
created_at TEXT NOT NULL,
|
|
450
|
+
updated_at TEXT NOT NULL,
|
|
451
|
+
PRIMARY KEY (message_id, durable_object_name)
|
|
452
|
+
)`;
|
|
453
|
+
} catch (error) {
|
|
454
|
+
console.error("[Agent] Failed to create ratings table", error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Rates a message.
|
|
459
|
+
* @param sql - The SQL function to use to execute the query.
|
|
460
|
+
* @param messageId - The ID of the message to rate.
|
|
461
|
+
* @param durable_object_name - The name of the Durable Object to rate the message for.
|
|
462
|
+
* @param rating - The rating to give the message.
|
|
463
|
+
* @param now - The date and time to use for the created_at and updated_at columns.
|
|
464
|
+
*/
|
|
465
|
+
function rateMessage(sql, messageId, durable_object_name, rating, comment, now = /* @__PURE__ */ new Date()) {
|
|
466
|
+
try {
|
|
467
|
+
sql`INSERT INTO assistant_messages_ratings (message_id, durable_object_name, rating, comment, created_at, updated_at)
|
|
468
|
+
VALUES (${messageId}, ${durable_object_name}, ${rating}, ${comment ?? null}, ${now.toISOString()}, ${now.toISOString()})
|
|
469
|
+
ON CONFLICT (message_id, durable_object_name) DO UPDATE SET
|
|
470
|
+
rating = excluded.rating,
|
|
471
|
+
comment = excluded.comment,
|
|
472
|
+
updated_at = excluded.updated_at`;
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error("[Agent] Failed to rate message", error);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Gets the ratings for a message.
|
|
479
|
+
* @param sql - The SQL function to use to execute the query.
|
|
480
|
+
* @param durable_object_name - The name of the Durable Object to get the ratings for.
|
|
481
|
+
* @returns A record of message IDs and their ratings.
|
|
482
|
+
*/
|
|
483
|
+
function getMessageRatings(sql, durable_object_name) {
|
|
484
|
+
try {
|
|
485
|
+
const ratings = sql`SELECT message_id, rating, comment FROM assistant_messages_ratings WHERE durable_object_name = ${durable_object_name}`;
|
|
486
|
+
return Object.fromEntries(ratings.map((row) => [row.message_id, {
|
|
487
|
+
rating: row.rating,
|
|
488
|
+
comment: row.comment
|
|
489
|
+
}]));
|
|
490
|
+
} catch {
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
480
495
|
//#region src/server/v2/agents/ChatAgent.ts
|
|
481
496
|
var ChatAgent = class extends Agent {
|
|
482
497
|
initialState = {
|
|
@@ -492,7 +507,7 @@ var ChatAgent = class extends Agent {
|
|
|
492
507
|
}
|
|
493
508
|
async onChatResponse(_result) {
|
|
494
509
|
const parent = await this.getParentAgent();
|
|
495
|
-
if (parent?.
|
|
510
|
+
if (parent?.recordChatTurn) await parent.recordChatTurn(this.name, this.messages);
|
|
496
511
|
}
|
|
497
512
|
/**
|
|
498
513
|
* Rate a message by its id.
|
|
@@ -504,10 +519,10 @@ var ChatAgent = class extends Agent {
|
|
|
504
519
|
return rateMessage(this.sql.bind(this), messageId, this.name, rating, comment);
|
|
505
520
|
}
|
|
506
521
|
/**
|
|
507
|
-
* Returns all message ratings for the current
|
|
508
|
-
* @returns All message ratings for the current
|
|
522
|
+
* Returns all message ratings for the current chat.
|
|
523
|
+
* @returns All message ratings for the current chat.
|
|
509
524
|
*/
|
|
510
|
-
@callable({ description: "Returns all message ratings for the current
|
|
525
|
+
@callable({ description: "Returns all message ratings for the current chat" }) async getMessageRatings() {
|
|
511
526
|
return getMessageRatings(this.sql.bind(this), this.name);
|
|
512
527
|
}
|
|
513
528
|
};
|