@aigne/core 1.72.0-beta.7 → 1.72.0-beta.9
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/CHANGELOG.md +27 -0
- package/lib/cjs/agents/agent.d.ts +2 -21
- package/lib/cjs/agents/agent.js +10 -15
- package/lib/cjs/agents/ai-agent.d.ts +20 -4
- package/lib/cjs/agents/ai-agent.js +37 -35
- package/lib/cjs/agents/image-model.d.ts +4 -4
- package/lib/cjs/agents/mcp-agent.d.ts +2 -2
- package/lib/cjs/agents/video-model.d.ts +4 -4
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/loader/agent-yaml.d.ts +1 -2
- package/lib/cjs/loader/agent-yaml.js +0 -8
- package/lib/cjs/loader/index.d.ts +2 -2
- package/lib/cjs/memory/recorder.d.ts +4 -4
- package/lib/cjs/memory/retriever.d.ts +4 -4
- package/lib/cjs/prompt/agent-session.d.ts +53 -0
- package/lib/cjs/prompt/agent-session.js +345 -0
- package/lib/cjs/prompt/compact/compactor.d.ts +7 -0
- package/lib/cjs/prompt/compact/compactor.js +48 -0
- package/lib/cjs/prompt/compact/types.d.ts +79 -0
- package/lib/cjs/prompt/compact/types.js +19 -0
- package/lib/cjs/prompt/context/afs/history.js +1 -1
- package/lib/cjs/prompt/prompt-builder.d.ts +6 -8
- package/lib/cjs/prompt/prompt-builder.js +67 -123
- package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.js +1 -0
- package/lib/cjs/prompt/template.d.ts +16 -16
- package/lib/dts/agents/agent.d.ts +2 -21
- package/lib/dts/agents/ai-agent.d.ts +20 -4
- package/lib/dts/agents/image-model.d.ts +4 -4
- package/lib/dts/agents/mcp-agent.d.ts +2 -2
- package/lib/dts/agents/video-model.d.ts +4 -4
- package/lib/dts/index.d.ts +1 -0
- package/lib/dts/loader/agent-yaml.d.ts +1 -2
- package/lib/dts/loader/index.d.ts +2 -2
- package/lib/dts/memory/recorder.d.ts +4 -4
- package/lib/dts/memory/retriever.d.ts +4 -4
- package/lib/dts/prompt/agent-session.d.ts +53 -0
- package/lib/dts/prompt/compact/compactor.d.ts +7 -0
- package/lib/dts/prompt/compact/types.d.ts +79 -0
- package/lib/dts/prompt/prompt-builder.d.ts +6 -8
- package/lib/dts/prompt/template.d.ts +16 -16
- package/lib/esm/agents/agent.d.ts +2 -21
- package/lib/esm/agents/agent.js +10 -15
- package/lib/esm/agents/ai-agent.d.ts +20 -4
- package/lib/esm/agents/ai-agent.js +37 -35
- package/lib/esm/agents/image-model.d.ts +4 -4
- package/lib/esm/agents/mcp-agent.d.ts +2 -2
- package/lib/esm/agents/video-model.d.ts +4 -4
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/loader/agent-yaml.d.ts +1 -2
- package/lib/esm/loader/agent-yaml.js +0 -8
- package/lib/esm/loader/index.d.ts +2 -2
- package/lib/esm/memory/recorder.d.ts +4 -4
- package/lib/esm/memory/retriever.d.ts +4 -4
- package/lib/esm/prompt/agent-session.d.ts +53 -0
- package/lib/esm/prompt/agent-session.js +308 -0
- package/lib/esm/prompt/compact/compactor.d.ts +7 -0
- package/lib/esm/prompt/compact/compactor.js +44 -0
- package/lib/esm/prompt/compact/types.d.ts +79 -0
- package/lib/esm/prompt/compact/types.js +16 -0
- package/lib/esm/prompt/context/afs/history.js +1 -1
- package/lib/esm/prompt/prompt-builder.d.ts +6 -8
- package/lib/esm/prompt/prompt-builder.js +68 -124
- package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.js +1 -0
- package/lib/esm/prompt/template.d.ts +16 -16
- package/package.json +4 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AFS } from "@aigne/afs";
|
|
2
|
+
import type { AgentInvokeOptions } from "../agents/agent.js";
|
|
3
|
+
import type { ChatModelInputMessage } from "../agents/chat-model.js";
|
|
4
|
+
import { type CompactConfig, type CompactContent, type EntryContent } from "./compact/types.js";
|
|
5
|
+
export type { CompactConfig, CompactContent, EntryContent };
|
|
6
|
+
export interface AgentSessionOptions {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
userId?: string;
|
|
9
|
+
agentId?: string;
|
|
10
|
+
afs?: AFS;
|
|
11
|
+
/**
|
|
12
|
+
* Compaction configuration
|
|
13
|
+
*/
|
|
14
|
+
compact?: CompactConfig;
|
|
15
|
+
}
|
|
16
|
+
export declare class AgentSession {
|
|
17
|
+
readonly sessionId: string;
|
|
18
|
+
readonly userId?: string;
|
|
19
|
+
readonly agentId?: string;
|
|
20
|
+
private afs?;
|
|
21
|
+
private historyModulePath?;
|
|
22
|
+
private compactConfig;
|
|
23
|
+
private runtimeState;
|
|
24
|
+
private initialized?;
|
|
25
|
+
private compactionPromise?;
|
|
26
|
+
constructor(options: AgentSessionOptions);
|
|
27
|
+
setSystemMessages(...messages: ChatModelInputMessage[]): Promise<void>;
|
|
28
|
+
getMessages(): Promise<ChatModelInputMessage[]>;
|
|
29
|
+
startMessage(input: unknown, message: ChatModelInputMessage, options: AgentInvokeOptions): Promise<void>;
|
|
30
|
+
endMessage(output: unknown, options: AgentInvokeOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Manually trigger compaction
|
|
33
|
+
*/
|
|
34
|
+
compact(options: AgentInvokeOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Internal method that performs the actual compaction
|
|
37
|
+
*/
|
|
38
|
+
private doCompact;
|
|
39
|
+
private maybeAutoCompact;
|
|
40
|
+
/**
|
|
41
|
+
* Estimate token count for an array of messages
|
|
42
|
+
*/
|
|
43
|
+
private estimateMessagesTokens;
|
|
44
|
+
/**
|
|
45
|
+
* Split entries into batches based on token limit
|
|
46
|
+
* Each batch will not exceed the specified maxTokens
|
|
47
|
+
*/
|
|
48
|
+
private splitIntoBatches;
|
|
49
|
+
appendCurrentMessages(...messages: ChatModelInputMessage[]): Promise<void>;
|
|
50
|
+
private ensureInitialized;
|
|
51
|
+
private initialize;
|
|
52
|
+
private initializeDefaultCompactor;
|
|
53
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AgentSession = void 0;
|
|
37
|
+
const afs_history_1 = require("@aigne/afs-history");
|
|
38
|
+
const uuid_1 = require("@aigne/uuid");
|
|
39
|
+
const ufo_1 = require("ufo");
|
|
40
|
+
const token_estimator_js_1 = require("../utils/token-estimator.js");
|
|
41
|
+
const type_utils_js_1 = require("../utils/type-utils.js");
|
|
42
|
+
const types_js_1 = require("./compact/types.js");
|
|
43
|
+
class AgentSession {
|
|
44
|
+
sessionId;
|
|
45
|
+
userId;
|
|
46
|
+
agentId;
|
|
47
|
+
afs;
|
|
48
|
+
historyModulePath;
|
|
49
|
+
compactConfig;
|
|
50
|
+
runtimeState;
|
|
51
|
+
initialized;
|
|
52
|
+
compactionPromise;
|
|
53
|
+
constructor(options) {
|
|
54
|
+
this.sessionId = options.sessionId;
|
|
55
|
+
this.userId = options.userId;
|
|
56
|
+
this.agentId = options.agentId;
|
|
57
|
+
this.afs = options.afs;
|
|
58
|
+
this.compactConfig = options.compact ?? {};
|
|
59
|
+
this.runtimeState = {
|
|
60
|
+
historyEntries: [],
|
|
61
|
+
currentEntry: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async setSystemMessages(...messages) {
|
|
65
|
+
await this.ensureInitialized();
|
|
66
|
+
this.runtimeState.systemMessages = messages;
|
|
67
|
+
}
|
|
68
|
+
async getMessages() {
|
|
69
|
+
await this.ensureInitialized();
|
|
70
|
+
const { systemMessages, compactSummary, historyEntries, currentEntry } = this.runtimeState;
|
|
71
|
+
const messages = [
|
|
72
|
+
...(systemMessages ?? []),
|
|
73
|
+
...(compactSummary
|
|
74
|
+
? [
|
|
75
|
+
{
|
|
76
|
+
role: "system",
|
|
77
|
+
content: `Previous conversation summary:\n${compactSummary}`,
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
: []),
|
|
81
|
+
...historyEntries.flatMap((entry) => entry.content?.messages ?? []),
|
|
82
|
+
...(currentEntry?.messages ?? []),
|
|
83
|
+
];
|
|
84
|
+
// Filter out thinking messages from content
|
|
85
|
+
return messages
|
|
86
|
+
.map((msg) => {
|
|
87
|
+
if (!msg.content || typeof msg.content === "string") {
|
|
88
|
+
return msg;
|
|
89
|
+
}
|
|
90
|
+
// Filter out thinking from UnionContent[]
|
|
91
|
+
const filteredContent = msg.content.filter((c) => !(c.type === "text" && c.isThinking));
|
|
92
|
+
if (filteredContent.length === 0)
|
|
93
|
+
return null;
|
|
94
|
+
return { ...msg, content: filteredContent };
|
|
95
|
+
})
|
|
96
|
+
.filter(type_utils_js_1.isNonNullable);
|
|
97
|
+
}
|
|
98
|
+
async startMessage(input, message, options) {
|
|
99
|
+
await this.ensureInitialized();
|
|
100
|
+
await this.maybeAutoCompact(options);
|
|
101
|
+
// Always wait for compaction to complete before starting a new message
|
|
102
|
+
// This ensures data consistency even in async compact mode
|
|
103
|
+
if (this.compactionPromise)
|
|
104
|
+
await this.compactionPromise;
|
|
105
|
+
this.runtimeState.currentEntry = { input, messages: [message] };
|
|
106
|
+
}
|
|
107
|
+
async endMessage(output, options) {
|
|
108
|
+
await this.ensureInitialized();
|
|
109
|
+
if (!this.runtimeState.currentEntry?.input ||
|
|
110
|
+
!this.runtimeState.currentEntry.messages?.length) {
|
|
111
|
+
throw new Error("No current entry to end. Call startMessage() first.");
|
|
112
|
+
}
|
|
113
|
+
this.runtimeState.currentEntry.output = output;
|
|
114
|
+
let newEntry;
|
|
115
|
+
if (this.afs && this.historyModulePath) {
|
|
116
|
+
newEntry = (await this.afs.write((0, ufo_1.joinURL)(this.historyModulePath, "new"), {
|
|
117
|
+
userId: this.userId,
|
|
118
|
+
sessionId: this.sessionId,
|
|
119
|
+
agentId: this.agentId,
|
|
120
|
+
content: this.runtimeState.currentEntry,
|
|
121
|
+
})).data;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const id = (0, uuid_1.v7)();
|
|
125
|
+
newEntry = {
|
|
126
|
+
id,
|
|
127
|
+
path: `/history/${id}`,
|
|
128
|
+
userId: this.userId,
|
|
129
|
+
sessionId: this.sessionId,
|
|
130
|
+
agentId: this.agentId,
|
|
131
|
+
content: this.runtimeState.currentEntry,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
this.runtimeState.historyEntries.push(newEntry);
|
|
135
|
+
this.runtimeState.currentEntry = null;
|
|
136
|
+
// Check if auto-compact should be triggered
|
|
137
|
+
await this.maybeAutoCompact(options);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Manually trigger compaction
|
|
141
|
+
*/
|
|
142
|
+
async compact(options) {
|
|
143
|
+
await this.ensureInitialized();
|
|
144
|
+
// If compaction is already in progress, wait for it to complete
|
|
145
|
+
if (this.compactionPromise) {
|
|
146
|
+
return this.compactionPromise;
|
|
147
|
+
}
|
|
148
|
+
// Start new compaction task
|
|
149
|
+
this.compactionPromise = this.doCompact(options).finally(() => {
|
|
150
|
+
this.compactionPromise = undefined;
|
|
151
|
+
});
|
|
152
|
+
return this.compactionPromise;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Internal method that performs the actual compaction
|
|
156
|
+
*/
|
|
157
|
+
async doCompact(options) {
|
|
158
|
+
const { compactor, keepRecentRatio } = this.compactConfig ?? {};
|
|
159
|
+
if (!compactor) {
|
|
160
|
+
throw new Error("Cannot compact without a compactor agent configured.");
|
|
161
|
+
}
|
|
162
|
+
const historyEntries = this.runtimeState.historyEntries;
|
|
163
|
+
if (historyEntries.length === 0)
|
|
164
|
+
return;
|
|
165
|
+
// Calculate token budget for keeping recent messages
|
|
166
|
+
const ratio = keepRecentRatio ?? types_js_1.DEFAULT_KEEP_RECENT_RATIO;
|
|
167
|
+
const maxTokens = this.compactConfig?.maxTokens ?? types_js_1.DEFAULT_MAX_TOKENS;
|
|
168
|
+
let keepTokenBudget = Math.floor(maxTokens * ratio);
|
|
169
|
+
// Calculate tokens for system messages
|
|
170
|
+
const systemTokens = (this.runtimeState.systemMessages ?? []).reduce((sum, msg) => {
|
|
171
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
|
|
172
|
+
return sum + (0, token_estimator_js_1.estimateTokens)(content);
|
|
173
|
+
}, 0);
|
|
174
|
+
// Calculate tokens for current entry messages
|
|
175
|
+
const currentTokens = (this.runtimeState.currentEntry?.messages ?? []).reduce((sum, msg) => {
|
|
176
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
|
|
177
|
+
return sum + (0, token_estimator_js_1.estimateTokens)(content);
|
|
178
|
+
}, 0);
|
|
179
|
+
// Subtract system and current tokens from budget
|
|
180
|
+
// This ensures total tokens (system + current + kept history) stays within ratio budget
|
|
181
|
+
keepTokenBudget = Math.max(0, keepTokenBudget - systemTokens - currentTokens);
|
|
182
|
+
// Find split point by iterating backwards from most recent entry
|
|
183
|
+
// The split point divides history into: [compact] | [keep]
|
|
184
|
+
let splitIndex = historyEntries.length; // Default: keep all (no compaction)
|
|
185
|
+
let accumulatedTokens = 0;
|
|
186
|
+
for (let i = historyEntries.length - 1; i >= 0; i--) {
|
|
187
|
+
const entry = historyEntries[i];
|
|
188
|
+
if (!entry)
|
|
189
|
+
continue;
|
|
190
|
+
const entryTokens = this.estimateMessagesTokens(entry.content?.messages ?? []);
|
|
191
|
+
// Check if adding this entry would exceed token budget
|
|
192
|
+
if (accumulatedTokens + entryTokens > keepTokenBudget) {
|
|
193
|
+
// Would exceed budget, split here (this entry and earlier ones will be compacted)
|
|
194
|
+
splitIndex = i + 1;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
// Can keep this entry, accumulate and continue
|
|
198
|
+
accumulatedTokens += entryTokens;
|
|
199
|
+
splitIndex = i;
|
|
200
|
+
}
|
|
201
|
+
// Split history at the found point
|
|
202
|
+
const entriesToCompact = historyEntries.slice(0, splitIndex);
|
|
203
|
+
const entriesToKeep = historyEntries.slice(splitIndex);
|
|
204
|
+
// If nothing to compact, return
|
|
205
|
+
if (entriesToCompact.length === 0) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const latestCompactedEntry = entriesToCompact.at(-1);
|
|
209
|
+
if (!latestCompactedEntry)
|
|
210
|
+
return;
|
|
211
|
+
// Split into batches to avoid context overflow
|
|
212
|
+
const batches = this.splitIntoBatches(entriesToCompact, maxTokens);
|
|
213
|
+
// Process batches incrementally, each summary becomes input for the next
|
|
214
|
+
let currentSummary = this.runtimeState.compactSummary;
|
|
215
|
+
for (const batch of batches) {
|
|
216
|
+
const result = await options.context.invoke(compactor, {
|
|
217
|
+
previousSummary: [currentSummary].filter(type_utils_js_1.isNonNullable),
|
|
218
|
+
messages: batch.flatMap((e) => e.content?.messages ?? []).filter(type_utils_js_1.isNonNullable),
|
|
219
|
+
});
|
|
220
|
+
currentSummary = result.summary;
|
|
221
|
+
}
|
|
222
|
+
// Write compact entry to AFS
|
|
223
|
+
if (this.afs && this.historyModulePath) {
|
|
224
|
+
await this.afs.write((0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/compact/new"), {
|
|
225
|
+
userId: this.userId,
|
|
226
|
+
agentId: this.agentId,
|
|
227
|
+
content: { summary: currentSummary },
|
|
228
|
+
metadata: {
|
|
229
|
+
latestEntryId: latestCompactedEntry.id,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Update runtime state: keep the summary and recent entries
|
|
234
|
+
this.runtimeState.compactSummary = currentSummary;
|
|
235
|
+
this.runtimeState.historyEntries = entriesToKeep;
|
|
236
|
+
}
|
|
237
|
+
async maybeAutoCompact(options) {
|
|
238
|
+
if (this.compactionPromise)
|
|
239
|
+
await this.compactionPromise;
|
|
240
|
+
if (!this.compactConfig)
|
|
241
|
+
return;
|
|
242
|
+
// Check if compaction is disabled
|
|
243
|
+
const mode = this.compactConfig.mode ?? types_js_1.DEFAULT_COMPACT_MODE;
|
|
244
|
+
if (mode === "disabled")
|
|
245
|
+
return;
|
|
246
|
+
const { compactor } = this.compactConfig;
|
|
247
|
+
const maxTokens = this.compactConfig.maxTokens ?? types_js_1.DEFAULT_MAX_TOKENS;
|
|
248
|
+
if (!compactor)
|
|
249
|
+
return;
|
|
250
|
+
const currentTokens = this.estimateMessagesTokens(await this.getMessages());
|
|
251
|
+
if (currentTokens >= maxTokens) {
|
|
252
|
+
this.compact(options);
|
|
253
|
+
const isAsync = this.compactConfig.async ?? types_js_1.DEFAULT_COMPACT_ASYNC;
|
|
254
|
+
if (!isAsync)
|
|
255
|
+
await this.compactionPromise;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Estimate token count for an array of messages
|
|
260
|
+
*/
|
|
261
|
+
estimateMessagesTokens(messages) {
|
|
262
|
+
return messages.reduce((sum, msg) => {
|
|
263
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
|
|
264
|
+
return sum + (0, token_estimator_js_1.estimateTokens)(content);
|
|
265
|
+
}, 0);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Split entries into batches based on token limit
|
|
269
|
+
* Each batch will not exceed the specified maxTokens
|
|
270
|
+
*/
|
|
271
|
+
splitIntoBatches(entries, maxTokens) {
|
|
272
|
+
const batches = [];
|
|
273
|
+
let currentBatch = [];
|
|
274
|
+
let currentTokens = 0;
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
const entryTokens = this.estimateMessagesTokens(entry.content?.messages ?? []);
|
|
277
|
+
// If adding this entry exceeds limit and we have entries in current batch, start new batch
|
|
278
|
+
if (currentTokens + entryTokens > maxTokens && currentBatch.length > 0) {
|
|
279
|
+
batches.push(currentBatch);
|
|
280
|
+
currentBatch = [entry];
|
|
281
|
+
currentTokens = entryTokens;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
currentBatch.push(entry);
|
|
285
|
+
currentTokens += entryTokens;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Add remaining entries
|
|
289
|
+
if (currentBatch.length > 0) {
|
|
290
|
+
batches.push(currentBatch);
|
|
291
|
+
}
|
|
292
|
+
return batches;
|
|
293
|
+
}
|
|
294
|
+
async appendCurrentMessages(...messages) {
|
|
295
|
+
await this.ensureInitialized();
|
|
296
|
+
if (!this.runtimeState.currentEntry || !this.runtimeState.currentEntry.messages?.length) {
|
|
297
|
+
throw new Error("No current entry to append messages. Call startMessage() first.");
|
|
298
|
+
}
|
|
299
|
+
this.runtimeState.currentEntry.messages.push(...messages);
|
|
300
|
+
}
|
|
301
|
+
async ensureInitialized() {
|
|
302
|
+
this.initialized ??= this.initialize();
|
|
303
|
+
await this.initialized;
|
|
304
|
+
}
|
|
305
|
+
async initialize() {
|
|
306
|
+
if (this.initialized)
|
|
307
|
+
return;
|
|
308
|
+
await this.initializeDefaultCompactor();
|
|
309
|
+
const historyModule = (await this.afs?.listModules())?.find((m) => m.module instanceof afs_history_1.AFSHistory);
|
|
310
|
+
this.historyModulePath = historyModule?.path;
|
|
311
|
+
if (this.afs && this.historyModulePath) {
|
|
312
|
+
// Load latest compact entry if exists
|
|
313
|
+
const compactPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/compact");
|
|
314
|
+
const compactResult = await this.afs.list(compactPath, {
|
|
315
|
+
filter: { userId: this.userId, agentId: this.agentId },
|
|
316
|
+
orderBy: [["createdAt", "desc"]],
|
|
317
|
+
limit: 1,
|
|
318
|
+
});
|
|
319
|
+
const latestCompact = compactResult.data[0];
|
|
320
|
+
if (latestCompact?.content?.summary) {
|
|
321
|
+
this.runtimeState.compactSummary = latestCompact.content.summary;
|
|
322
|
+
}
|
|
323
|
+
// Load history entries (after compact point if exists)
|
|
324
|
+
const afsEntries = (await this.afs.list((0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId), {
|
|
325
|
+
filter: {
|
|
326
|
+
userId: this.userId,
|
|
327
|
+
agentId: this.agentId,
|
|
328
|
+
// Only load entries after the latest compact
|
|
329
|
+
after: latestCompact?.createdAt?.toISOString(),
|
|
330
|
+
},
|
|
331
|
+
orderBy: [["createdAt", "desc"]],
|
|
332
|
+
// Set a very large limit to load all history entries
|
|
333
|
+
// The default limit is 10 which would cause history truncation
|
|
334
|
+
limit: 10000,
|
|
335
|
+
})).data;
|
|
336
|
+
this.runtimeState.historyEntries = afsEntries
|
|
337
|
+
.reverse()
|
|
338
|
+
.filter((entry) => (0, type_utils_js_1.isNonNullable)(entry.content));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async initializeDefaultCompactor() {
|
|
342
|
+
this.compactConfig.compactor ??= await Promise.resolve().then(() => __importStar(require("./compact/compactor.js"))).then((m) => new m.AISessionCompactor());
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
exports.AgentSession = AgentSession;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AIAgent, type AIAgentOptions } from "../../agents/ai-agent.js";
|
|
2
|
+
import type { CompactContent, CompactorInput } from "./types.js";
|
|
3
|
+
export interface CreateCompactorOptions extends AIAgentOptions<CompactorInput, CompactContent> {
|
|
4
|
+
}
|
|
5
|
+
export declare class AISessionCompactor extends AIAgent<CompactorInput, CompactContent> {
|
|
6
|
+
constructor(options?: CreateCompactorOptions);
|
|
7
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AISessionCompactor = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const ai_agent_js_1 = require("../../agents/ai-agent.js");
|
|
6
|
+
const type_utils_js_1 = require("../../utils/type-utils.js");
|
|
7
|
+
const COMPACTOR_INSTRUCTIONS = `\
|
|
8
|
+
You are a conversation summarizer. Your task is to create a concise but comprehensive summary of the conversation history provided.
|
|
9
|
+
|
|
10
|
+
## Conversation history
|
|
11
|
+
|
|
12
|
+
${"```"}yaml alt="previous-summary"
|
|
13
|
+
{{ previousSummary | yaml.stringify }}
|
|
14
|
+
${"```"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
${"```"}yaml alt="conversation-histories"
|
|
18
|
+
{{ messages | yaml.stringify }}
|
|
19
|
+
${"```"}
|
|
20
|
+
|
|
21
|
+
## Guidelines
|
|
22
|
+
|
|
23
|
+
1. Preserve key information, decisions, and context that would be needed for future conversation continuity
|
|
24
|
+
2. Include important facts, names, dates, and specific details mentioned
|
|
25
|
+
3. Summarize the user's goals and preferences expressed in the conversation
|
|
26
|
+
4. Note any pending tasks or follow-up items
|
|
27
|
+
5. Keep the summary focused and avoid unnecessary verbosity
|
|
28
|
+
6. Write in a neutral, factual tone
|
|
29
|
+
|
|
30
|
+
Output a single summary that captures the essence of the conversation.`;
|
|
31
|
+
class AISessionCompactor extends ai_agent_js_1.AIAgent {
|
|
32
|
+
constructor(options) {
|
|
33
|
+
super({
|
|
34
|
+
name: "SessionCompactor",
|
|
35
|
+
description: "Generates conversation summaries for session compaction",
|
|
36
|
+
inputSchema: zod_1.z.object({
|
|
37
|
+
previousSummary: (0, zod_1.optional)(zod_1.z.array(zod_1.z.string()).describe("List of previous conversation summaries")),
|
|
38
|
+
messages: zod_1.z.array(zod_1.z.any()),
|
|
39
|
+
}),
|
|
40
|
+
outputSchema: zod_1.z.object({
|
|
41
|
+
summary: zod_1.z.string().describe("A comprehensive summary of the conversation history"),
|
|
42
|
+
}),
|
|
43
|
+
instructions: COMPACTOR_INSTRUCTIONS,
|
|
44
|
+
...(0, type_utils_js_1.omitBy)(options ?? {}, (v) => (0, type_utils_js_1.isNil)(v)),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.AISessionCompactor = AISessionCompactor;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Agent, Message } from "../../agents/agent.js";
|
|
2
|
+
import type { ChatModelInputMessage } from "../../agents/chat-model.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default compaction mode
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_COMPACT_MODE: "auto";
|
|
7
|
+
/**
|
|
8
|
+
* Default maximum tokens before triggering compaction
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_MAX_TOKENS = 80000;
|
|
11
|
+
/**
|
|
12
|
+
* Default ratio of maxTokens to reserve for keeping recent messages
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_KEEP_RECENT_RATIO = 0.5;
|
|
15
|
+
/**
|
|
16
|
+
* Default async mode for compaction
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_COMPACT_ASYNC = true;
|
|
19
|
+
/**
|
|
20
|
+
* Content structure for history entries
|
|
21
|
+
*/
|
|
22
|
+
export interface EntryContent {
|
|
23
|
+
input?: unknown;
|
|
24
|
+
output?: unknown;
|
|
25
|
+
messages?: ChatModelInputMessage[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Output structure from the compactor agent
|
|
29
|
+
*/
|
|
30
|
+
export interface CompactContent extends Message {
|
|
31
|
+
summary: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Input structure for the compactor agent
|
|
35
|
+
*/
|
|
36
|
+
export interface CompactorInput extends Message {
|
|
37
|
+
previousSummary?: string[];
|
|
38
|
+
messages: ChatModelInputMessage[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Type alias for a compactor agent
|
|
42
|
+
*/
|
|
43
|
+
export type Compactor = Agent<CompactorInput, CompactContent>;
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for session compaction
|
|
46
|
+
*/
|
|
47
|
+
export interface CompactConfig {
|
|
48
|
+
/**
|
|
49
|
+
* Compaction mode
|
|
50
|
+
* @default DEFAULT_COMPACT_MODE ("auto")
|
|
51
|
+
*/
|
|
52
|
+
mode?: "auto" | "disabled";
|
|
53
|
+
/**
|
|
54
|
+
* Maximum tokens before triggering compaction
|
|
55
|
+
* @default DEFAULT_MAX_TOKENS (80000)
|
|
56
|
+
*/
|
|
57
|
+
maxTokens?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Ratio of maxTokens to reserve for keeping recent messages (0-1)
|
|
60
|
+
*
|
|
61
|
+
* Defines what portion of maxTokens budget should be allocated for
|
|
62
|
+
* preserving recent conversation history without compaction.
|
|
63
|
+
*
|
|
64
|
+
* @default 0.5 (50% of maxTokens)
|
|
65
|
+
* @example 0.5 means if maxTokens=80000, keep up to 40000 tokens of recent messages
|
|
66
|
+
*/
|
|
67
|
+
keepRecentRatio?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Whether to perform compaction asynchronously
|
|
70
|
+
* @default DEFAULT_COMPACT_ASYNC (true)
|
|
71
|
+
*/
|
|
72
|
+
async?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Agent that generates summaries from conversation entries
|
|
75
|
+
* Input: { entries: EntryContent[] }
|
|
76
|
+
* Output: { summary: string }
|
|
77
|
+
*/
|
|
78
|
+
compactor?: Compactor;
|
|
79
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_COMPACT_ASYNC = exports.DEFAULT_KEEP_RECENT_RATIO = exports.DEFAULT_MAX_TOKENS = exports.DEFAULT_COMPACT_MODE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Default compaction mode
|
|
6
|
+
*/
|
|
7
|
+
exports.DEFAULT_COMPACT_MODE = "auto";
|
|
8
|
+
/**
|
|
9
|
+
* Default maximum tokens before triggering compaction
|
|
10
|
+
*/
|
|
11
|
+
exports.DEFAULT_MAX_TOKENS = 80000;
|
|
12
|
+
/**
|
|
13
|
+
* Default ratio of maxTokens to reserve for keeping recent messages
|
|
14
|
+
*/
|
|
15
|
+
exports.DEFAULT_KEEP_RECENT_RATIO = 0.5;
|
|
16
|
+
/**
|
|
17
|
+
* Default async mode for compaction
|
|
18
|
+
*/
|
|
19
|
+
exports.DEFAULT_COMPACT_ASYNC = true;
|
|
@@ -12,7 +12,7 @@ async function getHistories({ filter, agent, }) {
|
|
|
12
12
|
return [];
|
|
13
13
|
const history = (await afs.list(historyModule.path, {
|
|
14
14
|
filter,
|
|
15
|
-
limit:
|
|
15
|
+
limit: 10,
|
|
16
16
|
orderBy: [["createdAt", "desc"]],
|
|
17
17
|
})).data;
|
|
18
18
|
return history
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { Agent, type Message } from "../agents/agent.js";
|
|
3
3
|
import { type AIAgent } from "../agents/ai-agent.js";
|
|
4
|
-
import {
|
|
4
|
+
import type { ChatModel, ChatModelInput, ChatModelInputMessage } from "../agents/chat-model.js";
|
|
5
5
|
import { type FileUnionContent } from "../agents/model.js";
|
|
6
6
|
import type { Context } from "../aigne/context.js";
|
|
7
|
+
import { AgentSession } from "./agent-session.js";
|
|
7
8
|
import { ChatMessagesTemplate } from "./template.js";
|
|
8
9
|
export interface PromptBuilderOptions {
|
|
9
10
|
instructions?: string | ChatMessagesTemplate;
|
|
@@ -28,7 +29,9 @@ export declare class PromptBuilder {
|
|
|
28
29
|
instructions?: string | ChatMessagesTemplate;
|
|
29
30
|
workingDir?: string;
|
|
30
31
|
copy(): PromptBuilder;
|
|
31
|
-
build(options: PromptBuildOptions): Promise<ChatModelInput & {
|
|
32
|
+
build(options: PromptBuildOptions): Promise<Omit<ChatModelInput, "messages"> & {
|
|
33
|
+
session: AgentSession;
|
|
34
|
+
userMessage: ChatModelInputMessage;
|
|
32
35
|
toolAgents?: Agent[];
|
|
33
36
|
}>;
|
|
34
37
|
buildPrompt(options: Pick<PromptBuildOptions, "input" | "context"> & {
|
|
@@ -39,13 +42,8 @@ export declare class PromptBuilder {
|
|
|
39
42
|
}>;
|
|
40
43
|
private getTemplateVariables;
|
|
41
44
|
private buildMessages;
|
|
45
|
+
private mergeMessages;
|
|
42
46
|
protected deprecatedMemories(message: string | undefined, options: PromptBuildOptions): Promise<ChatModelInputMessage[]>;
|
|
43
|
-
getHistories({ agentId, userId, sessionId, ...options }: PromptBuildOptions & {
|
|
44
|
-
agentId?: string;
|
|
45
|
-
userId?: string;
|
|
46
|
-
sessionId?: string;
|
|
47
|
-
}): Promise<ChatModelInputMessage[]>;
|
|
48
|
-
private refineMessages;
|
|
49
47
|
private convertMemoriesToMessages;
|
|
50
48
|
private buildResponseFormat;
|
|
51
49
|
private buildTools;
|