@economic/agents 2.10.4 → 2.10.6
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/README.md +3 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +154 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ React client: [`@economic/agents-react`](../react/README.md).
|
|
|
7
7
|
## The three classes
|
|
8
8
|
|
|
9
9
|
- **`Agent`** — the core. Runs the agent loop and keeps message history. Drive it over a WebSocket or programmatically (schedule, alarm, RPC). Most agents stop here.
|
|
10
|
-
- **`ChatAgent`** — `Agent` plus chat features: compaction and message feedback.
|
|
10
|
+
- **`ChatAgent`** — `Agent` plus chat features: model-only compaction that preserves the client transcript, and message feedback.
|
|
11
11
|
- **`Assistant`** — per-user shell over `ChatAgent`: create/list/delete chats, titles, summaries, retention.
|
|
12
12
|
|
|
13
13
|
```
|
|
@@ -188,7 +188,7 @@ You can also drive turns server-side — call `saveMessages` from a schedule or
|
|
|
188
188
|
|
|
189
189
|
## A chat agent
|
|
190
190
|
|
|
191
|
-
For a chat UI, extend `ChatAgent` instead of `Agent`. It adds compaction and message feedback, and connects to a browser with [`useChat`](../react/README.md#usechat) — no `Assistant` required. The DO name is the chat; address one per user, ticket, or whatever fits.
|
|
191
|
+
For a chat UI, extend `ChatAgent` instead of `Agent`. It adds model-only compaction (the stored/client transcript remains unchanged) and message feedback, and connects to a browser with [`useChat`](../react/README.md#usechat) — no `Assistant` required. The DO name is the chat; address one per user, ticket, or whatever fits.
|
|
192
192
|
|
|
193
193
|
```typescript
|
|
194
194
|
import { openai } from "@ai-sdk/openai";
|
|
@@ -578,7 +578,7 @@ Imported from `@economic/agents` unless noted.
|
|
|
578
578
|
| Export | Description |
|
|
579
579
|
| ----------- | -------------------------------------------------------- |
|
|
580
580
|
| `Agent` | Core agent base. Implement `getModel`/`getSystemPrompt`. |
|
|
581
|
-
| `ChatAgent` | `Agent` + compaction and message feedback.
|
|
581
|
+
| `ChatAgent` | `Agent` + model-only compaction and message feedback. |
|
|
582
582
|
| `Assistant` | Per-user manager of `ChatAgent` chats. |
|
|
583
583
|
|
|
584
584
|
### Helpers and types
|
package/dist/index.d.mts
CHANGED
|
@@ -186,7 +186,7 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
186
186
|
* @returns The remote skills to be loaded from SKILLS_BUCKET.
|
|
187
187
|
*/
|
|
188
188
|
protected getRemoteSkills(): string[];
|
|
189
|
-
|
|
189
|
+
protected pruneTurnMessages(messages: ModelMessage[]): ModelMessage[];
|
|
190
190
|
private _getTurnSystemPrompt;
|
|
191
191
|
private _prepareTools;
|
|
192
192
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages
|
|
|
4
4
|
import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
|
|
5
5
|
import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
6
6
|
import { genericObservability } from "agents/observability";
|
|
7
|
+
import { createCompactFunction, estimateMessageTokens, estimateStringTokens, sanitizeToolPairs, truncateOlderMessages } from "agents/experimental/memory/utils";
|
|
7
8
|
import { nanoid } from "nanoid";
|
|
8
|
-
import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
9
9
|
//#region src/server/features/auth/index.ts
|
|
10
10
|
function createJwtAuthConfig(config) {
|
|
11
11
|
return config;
|
|
@@ -1058,7 +1058,7 @@ var Agent = class extends Think {
|
|
|
1058
1058
|
return {
|
|
1059
1059
|
system: this._getTurnSystemPrompt(),
|
|
1060
1060
|
model: this.getModel(this._turn.context),
|
|
1061
|
-
messages: this.
|
|
1061
|
+
messages: this.pruneTurnMessages(ctx.messages),
|
|
1062
1062
|
tools: this._withTurnContextFallback(tools),
|
|
1063
1063
|
activeTools,
|
|
1064
1064
|
experimental_transform: buildSourcesTransform(),
|
|
@@ -1165,7 +1165,7 @@ var Agent = class extends Think {
|
|
|
1165
1165
|
tools: withFallbackToolContext(tools, () => snapshotContext),
|
|
1166
1166
|
activeTools,
|
|
1167
1167
|
frameworkToolNames: Object.keys(frameworkTools),
|
|
1168
|
-
messages: this.
|
|
1168
|
+
messages: this.pruneTurnMessages(messages)
|
|
1169
1169
|
};
|
|
1170
1170
|
} finally {
|
|
1171
1171
|
this._turn = previousTurn;
|
|
@@ -1183,7 +1183,7 @@ var Agent = class extends Think {
|
|
|
1183
1183
|
getRemoteSkills() {
|
|
1184
1184
|
return [];
|
|
1185
1185
|
}
|
|
1186
|
-
|
|
1186
|
+
pruneTurnMessages(messages) {
|
|
1187
1187
|
return pruneMessages({
|
|
1188
1188
|
messages,
|
|
1189
1189
|
toolCalls: [{
|
|
@@ -1978,13 +1978,130 @@ var Assistant = class extends Agent$1 {
|
|
|
1978
1978
|
agentClass: this.constructor.name
|
|
1979
1979
|
}) });
|
|
1980
1980
|
};
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1981
|
+
const COMPACTION_MAX_OUTPUT_TOKENS = 2e3;
|
|
1982
|
+
const createCompactFn = (model) => createCompactFunction({
|
|
1983
|
+
protectHead: 2,
|
|
1984
|
+
minTailMessages: 1,
|
|
1985
|
+
summarize: (prompt) => generateText({
|
|
1986
|
+
model,
|
|
1987
|
+
prompt,
|
|
1988
|
+
maxOutputTokens: COMPACTION_MAX_OUTPUT_TOKENS
|
|
1989
|
+
}).then((result) => result.text)
|
|
1990
|
+
});
|
|
1991
|
+
const MODEL_COMPACTION_MESSAGE_ID = "compaction_model_context";
|
|
1992
|
+
/**
|
|
1993
|
+
* Model compaction is deliberately stored separately from assistant_messages.
|
|
1994
|
+
* The transcript remains exact for clients; only model input uses the summary.
|
|
1995
|
+
*/
|
|
1996
|
+
function ensureModelCompactionTable(sql) {
|
|
1997
|
+
sql`CREATE TABLE IF NOT EXISTS economic_chat_model_compaction (
|
|
1998
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
1999
|
+
summary TEXT NOT NULL,
|
|
2000
|
+
from_message_id TEXT NOT NULL,
|
|
2001
|
+
to_message_id TEXT NOT NULL,
|
|
2002
|
+
updated_at INTEGER NOT NULL
|
|
2003
|
+
)`;
|
|
2004
|
+
}
|
|
2005
|
+
function getModelCompaction(sql) {
|
|
2006
|
+
const row = sql`SELECT summary, from_message_id, to_message_id
|
|
2007
|
+
FROM economic_chat_model_compaction
|
|
2008
|
+
WHERE id = 1`[0];
|
|
2009
|
+
return row ? {
|
|
2010
|
+
summary: row.summary,
|
|
2011
|
+
fromMessageId: row.from_message_id,
|
|
2012
|
+
toMessageId: row.to_message_id
|
|
2013
|
+
} : null;
|
|
2014
|
+
}
|
|
2015
|
+
function setModelCompaction(sql, state) {
|
|
2016
|
+
sql`INSERT INTO economic_chat_model_compaction (
|
|
2017
|
+
id, summary, from_message_id, to_message_id, updated_at
|
|
2018
|
+
) VALUES (
|
|
2019
|
+
1, ${state.summary}, ${state.fromMessageId}, ${state.toMessageId}, ${Date.now()}
|
|
2020
|
+
) ON CONFLICT (id) DO UPDATE SET
|
|
2021
|
+
summary = excluded.summary,
|
|
2022
|
+
from_message_id = excluded.from_message_id,
|
|
2023
|
+
to_message_id = excluded.to_message_id,
|
|
2024
|
+
updated_at = excluded.updated_at`;
|
|
2025
|
+
}
|
|
2026
|
+
function clearModelCompaction(sql) {
|
|
2027
|
+
sql`DELETE FROM economic_chat_model_compaction WHERE id = 1`;
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Move summaries created by the old Session overlay implementation into the
|
|
2031
|
+
* model-only store, then remove the overlays. Session retained the original
|
|
2032
|
+
* rows, so removing an overlay restores the exact transcript.
|
|
2033
|
+
*/
|
|
2034
|
+
function migrateSessionCompaction(sql) {
|
|
2035
|
+
const legacy = sql`SELECT
|
|
2036
|
+
summary, from_message_id, to_message_id
|
|
2037
|
+
FROM assistant_compactions
|
|
2038
|
+
WHERE session_id = ''
|
|
2039
|
+
ORDER BY created_at DESC, rowid DESC
|
|
2040
|
+
LIMIT 1`[0];
|
|
2041
|
+
if (!legacy) return false;
|
|
2042
|
+
if (!getModelCompaction(sql)) setModelCompaction(sql, {
|
|
2043
|
+
summary: legacy.summary,
|
|
2044
|
+
fromMessageId: legacy.from_message_id,
|
|
2045
|
+
toMessageId: legacy.to_message_id
|
|
2046
|
+
});
|
|
2047
|
+
sql`DELETE FROM assistant_compactions WHERE session_id = ''`;
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
function applyModelCompaction(messages, state) {
|
|
2051
|
+
const fromIndex = messages.findIndex((message) => message.id === state.fromMessageId);
|
|
2052
|
+
const toIndex = messages.findIndex((message) => message.id === state.toMessageId);
|
|
2053
|
+
if (fromIndex === -1 || toIndex < fromIndex) return null;
|
|
2054
|
+
return [
|
|
2055
|
+
...messages.slice(0, fromIndex),
|
|
2056
|
+
{
|
|
2057
|
+
id: MODEL_COMPACTION_MESSAGE_ID,
|
|
2058
|
+
role: "assistant",
|
|
2059
|
+
parts: [{
|
|
2060
|
+
type: "text",
|
|
2061
|
+
text: state.summary
|
|
2062
|
+
}]
|
|
2063
|
+
},
|
|
2064
|
+
...messages.slice(toIndex + 1)
|
|
2065
|
+
];
|
|
2066
|
+
}
|
|
2067
|
+
/** Build the compacted projection used only for the next model call. */
|
|
2068
|
+
async function prepareModelHistory(messages, existingState, compact, systemPrompt = "") {
|
|
2069
|
+
let state = existingState;
|
|
2070
|
+
let projected = state ? applyModelCompaction(messages, state) : messages;
|
|
2071
|
+
let stateChanged = false;
|
|
2072
|
+
if (!projected) {
|
|
2073
|
+
state = null;
|
|
2074
|
+
projected = messages;
|
|
2075
|
+
stateChanged = true;
|
|
2076
|
+
}
|
|
2077
|
+
if (estimateMessageTokens(projected) + estimateStringTokens(systemPrompt) <= 1e5) return {
|
|
2078
|
+
messages: projected,
|
|
2079
|
+
state,
|
|
2080
|
+
stateChanged
|
|
2081
|
+
};
|
|
2082
|
+
const result = await compact(projected);
|
|
2083
|
+
if (!result) return {
|
|
2084
|
+
messages: projected,
|
|
2085
|
+
state,
|
|
2086
|
+
stateChanged
|
|
2087
|
+
};
|
|
2088
|
+
const nextState = {
|
|
2089
|
+
summary: result.summary,
|
|
2090
|
+
fromMessageId: state?.fromMessageId ?? result.fromMessageId,
|
|
2091
|
+
toMessageId: result.toMessageId
|
|
2092
|
+
};
|
|
2093
|
+
const nextProjection = applyModelCompaction(messages, nextState);
|
|
2094
|
+
if (!nextProjection) return {
|
|
2095
|
+
messages: projected,
|
|
2096
|
+
state,
|
|
2097
|
+
stateChanged
|
|
2098
|
+
};
|
|
2099
|
+
return {
|
|
2100
|
+
messages: nextProjection,
|
|
2101
|
+
state: nextState,
|
|
2102
|
+
stateChanged: true
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
1988
2105
|
/**
|
|
1989
2106
|
* Ensures that the feedback table exists.
|
|
1990
2107
|
* @param sql - The SQL function to use to execute the query.
|
|
@@ -2189,14 +2306,38 @@ var ChatAgent = class extends Agent {
|
|
|
2189
2306
|
async onStart() {
|
|
2190
2307
|
await super.onStart();
|
|
2191
2308
|
ensureFeedbackTableExists(this.sql.bind(this));
|
|
2309
|
+
ensureModelCompactionTable(this.sql.bind(this));
|
|
2310
|
+
if (migrateSessionCompaction(this.sql.bind(this))) await this.syncMessagesFromStorage();
|
|
2192
2311
|
}
|
|
2193
2312
|
configureSession(session) {
|
|
2194
|
-
return super.configureSession(session)
|
|
2313
|
+
return super.configureSession(session);
|
|
2195
2314
|
}
|
|
2196
2315
|
async beforeTurn(ctx) {
|
|
2197
2316
|
const parent = await this.getParentAgent();
|
|
2198
2317
|
if (parent?.registerChat) await parent.registerChat(this.name);
|
|
2199
|
-
|
|
2318
|
+
const config = await super.beforeTurn(ctx) ?? {};
|
|
2319
|
+
try {
|
|
2320
|
+
const prepared = await prepareModelHistory(this.messages, getModelCompaction(this.sql.bind(this)), createCompactFn(config.model ?? ctx.model), config.system ?? ctx.system);
|
|
2321
|
+
if (prepared.stateChanged) if (prepared.state) setModelCompaction(this.sql.bind(this), prepared.state);
|
|
2322
|
+
else clearModelCompaction(this.sql.bind(this));
|
|
2323
|
+
if (prepared.state) {
|
|
2324
|
+
const tools = config.tools ? {
|
|
2325
|
+
...ctx.tools,
|
|
2326
|
+
...config.tools
|
|
2327
|
+
} : ctx.tools;
|
|
2328
|
+
const modelMessages = await convertToModelMessages(truncateOlderMessages(sanitizeToolPairs(prepared.messages)), {
|
|
2329
|
+
tools,
|
|
2330
|
+
ignoreIncompleteToolCalls: true
|
|
2331
|
+
});
|
|
2332
|
+
return {
|
|
2333
|
+
...config,
|
|
2334
|
+
messages: this.pruneTurnMessages(modelMessages)
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
} catch (error) {
|
|
2338
|
+
console.error("[ChatAgent] Failed to prepare model-only compaction", error);
|
|
2339
|
+
}
|
|
2340
|
+
return config;
|
|
2200
2341
|
}
|
|
2201
2342
|
async onChatResponse(result) {
|
|
2202
2343
|
recordTurnCompletedAnalytics(this.env.AGENTS_ANALYTICS, {
|