@adminforth/agent 1.16.4 → 1.18.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/agent/checkpointer.ts +328 -0
- package/agent/simpleAgent.ts +14 -5
- package/build.log +2 -2
- package/custom/ChatSurface.vue +2 -0
- package/dist/agent/checkpointer.js +236 -0
- package/dist/agent/simpleAgent.js +4 -4
- package/dist/custom/ChatSurface.vue +2 -0
- package/dist/index.js +35 -9
- package/index.ts +51 -10
- package/package.json +2 -1
- package/types.ts +28 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
+
import {
|
|
3
|
+
BaseCheckpointSaver,
|
|
4
|
+
type Checkpoint,
|
|
5
|
+
type CheckpointPendingWrite,
|
|
6
|
+
type CheckpointMetadata,
|
|
7
|
+
type CheckpointTuple,
|
|
8
|
+
type PendingWrite,
|
|
9
|
+
WRITES_IDX_MAP,
|
|
10
|
+
} from "@langchain/langgraph-checkpoint";
|
|
11
|
+
import type { PluginOptions } from "../types.js";
|
|
12
|
+
import { Filters } from "adminforth";
|
|
13
|
+
|
|
14
|
+
const ROOT_CHECKPOINT_NAMESPACE = "__root__";
|
|
15
|
+
|
|
16
|
+
export class AdminForthCheckpointSaver extends BaseCheckpointSaver {
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly adminforth: any,
|
|
19
|
+
private readonly pluginOptions: PluginOptions,
|
|
20
|
+
) {
|
|
21
|
+
super();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private get resourceConfig() {
|
|
25
|
+
const resource = this.pluginOptions.checkpointResource;
|
|
26
|
+
if (!resource) {
|
|
27
|
+
throw new Error("checkpointResource is not configured");
|
|
28
|
+
}
|
|
29
|
+
return resource;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private resource() {
|
|
33
|
+
return this.adminforth.resource(this.resourceConfig.resourceId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private serialize(value: unknown): string | null {
|
|
37
|
+
if (value === undefined || value === null) return null;
|
|
38
|
+
return JSON.stringify(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private deserialize<T>(value: unknown): T | null {
|
|
42
|
+
if (value === undefined || value === null) return null;
|
|
43
|
+
if (typeof value === "string") {
|
|
44
|
+
return JSON.parse(value) as T;
|
|
45
|
+
}
|
|
46
|
+
return value as T;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private now(): string {
|
|
50
|
+
return new Date().toISOString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private encodeCheckpointNamespace(checkpointNs: string): string {
|
|
54
|
+
return checkpointNs === "" ? ROOT_CHECKPOINT_NAMESPACE : checkpointNs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private decodeCheckpointNamespace(checkpointNs: unknown): string {
|
|
58
|
+
return checkpointNs === ROOT_CHECKPOINT_NAMESPACE ? "" : String(checkpointNs ?? "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private getConfigValues(config: RunnableConfig) {
|
|
62
|
+
const configurable = (config.configurable ?? {}) as Record<string, unknown>;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
threadId: String(configurable.thread_id ?? ""),
|
|
66
|
+
checkpointNs: String(configurable.checkpoint_ns ?? ""),
|
|
67
|
+
checkpointId: configurable.checkpoint_id
|
|
68
|
+
? String(configurable.checkpoint_id)
|
|
69
|
+
: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private buildConfig(threadId: string, checkpointNs: string, checkpointId: string): RunnableConfig {
|
|
74
|
+
return {
|
|
75
|
+
configurable: {
|
|
76
|
+
thread_id: threadId,
|
|
77
|
+
checkpoint_ns: checkpointNs,
|
|
78
|
+
checkpoint_id: checkpointId,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private buildCheckpointRowId(threadId: string, checkpointNs: string, checkpointId: string) {
|
|
84
|
+
return `cp:${threadId}:${checkpointNs}:${checkpointId}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private buildWritesRowId(
|
|
88
|
+
threadId: string,
|
|
89
|
+
checkpointNs: string,
|
|
90
|
+
checkpointId: string,
|
|
91
|
+
taskId: string,
|
|
92
|
+
seq: number,
|
|
93
|
+
) {
|
|
94
|
+
return `wr:${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${seq}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private getWriteIndex(channel: string, index: number): number {
|
|
98
|
+
return WRITES_IDX_MAP[channel] ?? index;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private isDuplicateCheckpointWriteError(error: unknown): boolean {
|
|
102
|
+
return error instanceof Error && error.message.includes("UNIQUE constraint failed");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async put(
|
|
106
|
+
config: RunnableConfig,
|
|
107
|
+
checkpoint: Checkpoint,
|
|
108
|
+
metadata: CheckpointMetadata,
|
|
109
|
+
_newVersions: Record<string, unknown>,
|
|
110
|
+
): Promise<RunnableConfig> {
|
|
111
|
+
const r = this.resourceConfig;
|
|
112
|
+
const { threadId, checkpointNs } = this.getConfigValues(config);
|
|
113
|
+
const checkpointId = String((checkpoint as any).id);
|
|
114
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
115
|
+
|
|
116
|
+
const parentCheckpointId = this.getConfigValues(config).checkpointId;
|
|
117
|
+
const createdAt = this.now();
|
|
118
|
+
|
|
119
|
+
await this.resource().create({
|
|
120
|
+
[r.idField]: this.buildCheckpointRowId(threadId, storedCheckpointNs, checkpointId),
|
|
121
|
+
[r.threadIdField]: threadId,
|
|
122
|
+
[r.checkpointNamespaceField]: storedCheckpointNs,
|
|
123
|
+
[r.checkpointIdField]: checkpointId,
|
|
124
|
+
[r.parentCheckpointIdField]: parentCheckpointId,
|
|
125
|
+
[r.rowKindField]: "checkpoint",
|
|
126
|
+
[r.taskIdField]: null,
|
|
127
|
+
[r.sequenceField]: 0,
|
|
128
|
+
[r.createdAtField]: createdAt,
|
|
129
|
+
[r.checkpointPayloadField]: this.serialize(checkpoint),
|
|
130
|
+
[r.metadataPayloadField]: this.serialize(metadata),
|
|
131
|
+
[r.writesPayloadField]: null,
|
|
132
|
+
[r.schemaVersionField]: 1,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return this.buildConfig(threadId, checkpointNs, checkpointId);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async putWrites(
|
|
139
|
+
config: RunnableConfig,
|
|
140
|
+
writes: PendingWrite[],
|
|
141
|
+
taskId: string,
|
|
142
|
+
): Promise<void> {
|
|
143
|
+
const r = this.resourceConfig;
|
|
144
|
+
const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
|
|
145
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
146
|
+
|
|
147
|
+
if (!checkpointId) {
|
|
148
|
+
throw new Error("putWrites requires checkpoint_id in config");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const createdAt = this.now();
|
|
152
|
+
|
|
153
|
+
await Promise.all(
|
|
154
|
+
writes.map(async ([channel, value], index) => {
|
|
155
|
+
const writeIndex = this.getWriteIndex(channel, index);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await this.resource().create({
|
|
159
|
+
[r.idField]: this.buildWritesRowId(
|
|
160
|
+
threadId,
|
|
161
|
+
storedCheckpointNs,
|
|
162
|
+
checkpointId,
|
|
163
|
+
taskId,
|
|
164
|
+
writeIndex,
|
|
165
|
+
),
|
|
166
|
+
[r.threadIdField]: threadId,
|
|
167
|
+
[r.checkpointNamespaceField]: storedCheckpointNs,
|
|
168
|
+
[r.checkpointIdField]: checkpointId,
|
|
169
|
+
[r.parentCheckpointIdField]: null,
|
|
170
|
+
[r.rowKindField]: "writes",
|
|
171
|
+
[r.taskIdField]: taskId,
|
|
172
|
+
[r.sequenceField]: writeIndex,
|
|
173
|
+
[r.createdAtField]: createdAt,
|
|
174
|
+
[r.checkpointPayloadField]: null,
|
|
175
|
+
[r.metadataPayloadField]: null,
|
|
176
|
+
[r.writesPayloadField]: this.serialize([channel, value] satisfies PendingWrite),
|
|
177
|
+
[r.schemaVersionField]: 1,
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (!this.isDuplicateCheckpointWriteError(error)) {
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {
|
|
189
|
+
const r = this.resourceConfig;
|
|
190
|
+
const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
|
|
191
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
192
|
+
|
|
193
|
+
const checkpointRows = await this.resource().list(
|
|
194
|
+
checkpointId
|
|
195
|
+
? Filters.AND(
|
|
196
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
197
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
198
|
+
Filters.EQ(r.checkpointIdField, checkpointId),
|
|
199
|
+
Filters.EQ(r.rowKindField, "checkpoint"),
|
|
200
|
+
)
|
|
201
|
+
: Filters.AND(
|
|
202
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
203
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
204
|
+
Filters.EQ(r.rowKindField, "checkpoint"),
|
|
205
|
+
),
|
|
206
|
+
1,
|
|
207
|
+
undefined,
|
|
208
|
+
[{ field: r.checkpointIdField, direction: "desc" }],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const checkpointRow = checkpointRows[0];
|
|
212
|
+
if (!checkpointRow) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const resolvedCheckpointId = String(checkpointRow[r.checkpointIdField]);
|
|
217
|
+
|
|
218
|
+
const writesRows = await this.resource().list(
|
|
219
|
+
Filters.AND(
|
|
220
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
221
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
222
|
+
Filters.EQ(r.checkpointIdField, resolvedCheckpointId),
|
|
223
|
+
Filters.EQ(r.rowKindField, "writes"),
|
|
224
|
+
),
|
|
225
|
+
undefined,
|
|
226
|
+
undefined,
|
|
227
|
+
[{ field: r.sequenceField, direction: "asc" }],
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const pendingWrites: CheckpointPendingWrite[] = writesRows.flatMap((row) => {
|
|
231
|
+
const taskId = String(row[r.taskIdField] ?? "");
|
|
232
|
+
const write = this.deserialize<PendingWrite>(row[r.writesPayloadField]);
|
|
233
|
+
if (!write) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const [channel, value] = write;
|
|
238
|
+
return [[taskId, channel, value]];
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const parentCheckpointId = checkpointRow[r.parentCheckpointIdField]
|
|
242
|
+
? String(checkpointRow[r.parentCheckpointIdField])
|
|
243
|
+
: null;
|
|
244
|
+
|
|
245
|
+
const tuple: CheckpointTuple = {
|
|
246
|
+
config: this.buildConfig(threadId, checkpointNs, resolvedCheckpointId),
|
|
247
|
+
checkpoint: this.deserialize<Checkpoint>(
|
|
248
|
+
checkpointRow[r.checkpointPayloadField],
|
|
249
|
+
) as Checkpoint,
|
|
250
|
+
metadata: (this.deserialize<CheckpointMetadata>(
|
|
251
|
+
checkpointRow[r.metadataPayloadField],
|
|
252
|
+
) ?? {}) as CheckpointMetadata,
|
|
253
|
+
parentConfig: parentCheckpointId
|
|
254
|
+
? this.buildConfig(threadId, checkpointNs, parentCheckpointId)
|
|
255
|
+
: undefined,
|
|
256
|
+
pendingWrites,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return tuple;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async *list(
|
|
263
|
+
config: RunnableConfig,
|
|
264
|
+
options?: {
|
|
265
|
+
before?: RunnableConfig;
|
|
266
|
+
limit?: number;
|
|
267
|
+
},
|
|
268
|
+
): AsyncGenerator<CheckpointTuple> {
|
|
269
|
+
const r = this.resourceConfig;
|
|
270
|
+
const { threadId, checkpointNs } = this.getConfigValues(config);
|
|
271
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
272
|
+
const beforeCheckpointId = options?.before
|
|
273
|
+
? this.getConfigValues(options.before).checkpointId
|
|
274
|
+
: null;
|
|
275
|
+
|
|
276
|
+
const filters: Filters[] = [
|
|
277
|
+
Filters.EQ(r.rowKindField, "checkpoint"),
|
|
278
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
279
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
if (beforeCheckpointId) {
|
|
283
|
+
filters.push(Filters.LT(r.checkpointIdField, beforeCheckpointId));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const rows = await this.resource().list(
|
|
287
|
+
Filters.AND(...filters),
|
|
288
|
+
options?.limit,
|
|
289
|
+
undefined,
|
|
290
|
+
[{ field: r.checkpointIdField, direction: "desc" }],
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
for (const row of rows) {
|
|
294
|
+
const tuple = await this.getTuple(
|
|
295
|
+
this.buildConfig(
|
|
296
|
+
String(row[r.threadIdField]),
|
|
297
|
+
this.decodeCheckpointNamespace(row[r.checkpointNamespaceField]),
|
|
298
|
+
String(row[r.checkpointIdField]),
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (tuple) {
|
|
303
|
+
yield tuple;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async deleteThread(threadId: string, checkpointNs = ""): Promise<void> {
|
|
309
|
+
const r = this.resourceConfig;
|
|
310
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
311
|
+
|
|
312
|
+
const rows = await this.resource().list(
|
|
313
|
+
Filters.AND(
|
|
314
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
315
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
316
|
+
),
|
|
317
|
+
undefined,
|
|
318
|
+
undefined,
|
|
319
|
+
[{ field: r.createdAtField, direction: "desc" }],
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
for (const row of rows) {
|
|
323
|
+
await this.adminforth
|
|
324
|
+
.resource(this.pluginOptions.checkpointResource!.resourceId)
|
|
325
|
+
.delete(row[r.idField]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
package/agent/simpleAgent.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createAgent, summarizationMiddleware } from "langchain";
|
|
2
2
|
import { logger, type AdminUser, type CompletionAdapter } from "adminforth";
|
|
3
3
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
4
|
-
import {
|
|
4
|
+
import {type BaseCheckpointSaver, type Messages } from "@langchain/langgraph";
|
|
5
5
|
import type { LLMResult } from "@langchain/core/outputs";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { ChatOpenAI } from "@langchain/openai";
|
|
@@ -15,8 +15,6 @@ import { createOpenAiResponsesContinuationMiddleware } from "./middleware/openAi
|
|
|
15
15
|
import type { ApiBasedTool } from "../apiBasedTools.js";
|
|
16
16
|
import type { ToolCallEventSink } from "./toolCallEvents.js";
|
|
17
17
|
|
|
18
|
-
const checkpointer = new MemorySaver();
|
|
19
|
-
|
|
20
18
|
export const contextSchema = z.object({
|
|
21
19
|
adminUser: z.custom<AdminUser>(),
|
|
22
20
|
userTimeZone: z.string(),
|
|
@@ -36,6 +34,8 @@ type OpenAIBackedCompletionAdapter = CompletionAdapter & {
|
|
|
36
34
|
};
|
|
37
35
|
};
|
|
38
36
|
|
|
37
|
+
type OpenAiReasoningConfig = Record<string, unknown>;
|
|
38
|
+
|
|
39
39
|
type LlmOutputTokenUsage = {
|
|
40
40
|
promptTokens?: unknown;
|
|
41
41
|
completionTokens?: unknown;
|
|
@@ -178,7 +178,14 @@ export function createAgentChatModel(params: {
|
|
|
178
178
|
|
|
179
179
|
const model = params.modelName ?? options.model ?? "gpt-5-nano";
|
|
180
180
|
const baseURL = options.baseURL ?? options.baseUrl;
|
|
181
|
-
const reasoning = options.extraRequestBodyParameters
|
|
181
|
+
const reasoning = options.extraRequestBodyParameters
|
|
182
|
+
?.reasoning as OpenAiReasoningConfig | undefined;
|
|
183
|
+
const reasoningConfig = reasoning
|
|
184
|
+
? {
|
|
185
|
+
...reasoning,
|
|
186
|
+
summary: "auto",
|
|
187
|
+
}
|
|
188
|
+
: undefined;
|
|
182
189
|
|
|
183
190
|
// @ts-ignore
|
|
184
191
|
return new ChatOpenAI({
|
|
@@ -192,7 +199,7 @@ export function createAgentChatModel(params: {
|
|
|
192
199
|
|
|
193
200
|
promptCacheRetention: "in_memory",
|
|
194
201
|
|
|
195
|
-
...(
|
|
202
|
+
...(reasoningConfig ? { reasoning: reasoningConfig } : {}),
|
|
196
203
|
...(typeof options.timeoutMs === "number"
|
|
197
204
|
? { timeout: options.timeoutMs }
|
|
198
205
|
: {}),
|
|
@@ -210,6 +217,7 @@ export async function callAgent(params: {
|
|
|
210
217
|
name: string;
|
|
211
218
|
model: ChatOpenAI;
|
|
212
219
|
summaryModel: ChatOpenAI;
|
|
220
|
+
checkpointer?: BaseCheckpointSaver;
|
|
213
221
|
messages: Messages;
|
|
214
222
|
adminUser: AdminUser;
|
|
215
223
|
apiBasedTools: Record<string, ApiBasedTool>;
|
|
@@ -224,6 +232,7 @@ export async function callAgent(params: {
|
|
|
224
232
|
name,
|
|
225
233
|
model,
|
|
226
234
|
summaryModel,
|
|
235
|
+
checkpointer,
|
|
227
236
|
messages,
|
|
228
237
|
adminUser,
|
|
229
238
|
apiBasedTools,
|
package/build.log
CHANGED
|
@@ -31,5 +31,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
31
31
|
custom/skills/mutate_data/
|
|
32
32
|
custom/skills/mutate_data/SKILL.md
|
|
33
33
|
|
|
34
|
-
sent 185,
|
|
35
|
-
total size is 183,
|
|
34
|
+
sent 185,196 bytes received 432 bytes 371,256.00 bytes/sec
|
|
35
|
+
total size is 183,434 speedup is 0.99
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -193,6 +193,7 @@ const props = defineProps<{
|
|
|
193
193
|
name: string;
|
|
194
194
|
}>;
|
|
195
195
|
defaultModeName: string | null;
|
|
196
|
+
stickByDefault: boolean;
|
|
196
197
|
}
|
|
197
198
|
}>();
|
|
198
199
|
|
|
@@ -245,6 +246,7 @@ onMounted(async () => {
|
|
|
245
246
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
246
247
|
agentStore.regisrerTextInput(textInput.value);
|
|
247
248
|
textInput.value?.focus();
|
|
249
|
+
agentStore.setIsTeleportedToBody(props.meta.stickByDefault);
|
|
248
250
|
await agentStore.fetchSessionsList();
|
|
249
251
|
});
|
|
250
252
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
11
|
+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
14
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
15
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
16
|
+
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
17
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
18
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
19
|
+
function fulfill(value) { resume("next", value); }
|
|
20
|
+
function reject(value) { resume("throw", value); }
|
|
21
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
22
|
+
};
|
|
23
|
+
import { BaseCheckpointSaver, WRITES_IDX_MAP, } from "@langchain/langgraph-checkpoint";
|
|
24
|
+
import { Filters } from "adminforth";
|
|
25
|
+
const ROOT_CHECKPOINT_NAMESPACE = "__root__";
|
|
26
|
+
export class AdminForthCheckpointSaver extends BaseCheckpointSaver {
|
|
27
|
+
constructor(adminforth, pluginOptions) {
|
|
28
|
+
super();
|
|
29
|
+
this.adminforth = adminforth;
|
|
30
|
+
this.pluginOptions = pluginOptions;
|
|
31
|
+
}
|
|
32
|
+
get resourceConfig() {
|
|
33
|
+
const resource = this.pluginOptions.checkpointResource;
|
|
34
|
+
if (!resource) {
|
|
35
|
+
throw new Error("checkpointResource is not configured");
|
|
36
|
+
}
|
|
37
|
+
return resource;
|
|
38
|
+
}
|
|
39
|
+
resource() {
|
|
40
|
+
return this.adminforth.resource(this.resourceConfig.resourceId);
|
|
41
|
+
}
|
|
42
|
+
serialize(value) {
|
|
43
|
+
if (value === undefined || value === null)
|
|
44
|
+
return null;
|
|
45
|
+
return JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
deserialize(value) {
|
|
48
|
+
if (value === undefined || value === null)
|
|
49
|
+
return null;
|
|
50
|
+
if (typeof value === "string") {
|
|
51
|
+
return JSON.parse(value);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
now() {
|
|
56
|
+
return new Date().toISOString();
|
|
57
|
+
}
|
|
58
|
+
encodeCheckpointNamespace(checkpointNs) {
|
|
59
|
+
return checkpointNs === "" ? ROOT_CHECKPOINT_NAMESPACE : checkpointNs;
|
|
60
|
+
}
|
|
61
|
+
decodeCheckpointNamespace(checkpointNs) {
|
|
62
|
+
return checkpointNs === ROOT_CHECKPOINT_NAMESPACE ? "" : String(checkpointNs !== null && checkpointNs !== void 0 ? checkpointNs : "");
|
|
63
|
+
}
|
|
64
|
+
getConfigValues(config) {
|
|
65
|
+
var _a, _b, _c;
|
|
66
|
+
const configurable = ((_a = config.configurable) !== null && _a !== void 0 ? _a : {});
|
|
67
|
+
return {
|
|
68
|
+
threadId: String((_b = configurable.thread_id) !== null && _b !== void 0 ? _b : ""),
|
|
69
|
+
checkpointNs: String((_c = configurable.checkpoint_ns) !== null && _c !== void 0 ? _c : ""),
|
|
70
|
+
checkpointId: configurable.checkpoint_id
|
|
71
|
+
? String(configurable.checkpoint_id)
|
|
72
|
+
: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
buildConfig(threadId, checkpointNs, checkpointId) {
|
|
76
|
+
return {
|
|
77
|
+
configurable: {
|
|
78
|
+
thread_id: threadId,
|
|
79
|
+
checkpoint_ns: checkpointNs,
|
|
80
|
+
checkpoint_id: checkpointId,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
buildCheckpointRowId(threadId, checkpointNs, checkpointId) {
|
|
85
|
+
return `cp:${threadId}:${checkpointNs}:${checkpointId}`;
|
|
86
|
+
}
|
|
87
|
+
buildWritesRowId(threadId, checkpointNs, checkpointId, taskId, seq) {
|
|
88
|
+
return `wr:${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${seq}`;
|
|
89
|
+
}
|
|
90
|
+
getWriteIndex(channel, index) {
|
|
91
|
+
var _a;
|
|
92
|
+
return (_a = WRITES_IDX_MAP[channel]) !== null && _a !== void 0 ? _a : index;
|
|
93
|
+
}
|
|
94
|
+
isDuplicateCheckpointWriteError(error) {
|
|
95
|
+
return error instanceof Error && error.message.includes("UNIQUE constraint failed");
|
|
96
|
+
}
|
|
97
|
+
put(config, checkpoint, metadata, _newVersions) {
|
|
98
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
const r = this.resourceConfig;
|
|
100
|
+
const { threadId, checkpointNs } = this.getConfigValues(config);
|
|
101
|
+
const checkpointId = String(checkpoint.id);
|
|
102
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
103
|
+
const parentCheckpointId = this.getConfigValues(config).checkpointId;
|
|
104
|
+
const createdAt = this.now();
|
|
105
|
+
yield this.resource().create({
|
|
106
|
+
[r.idField]: this.buildCheckpointRowId(threadId, storedCheckpointNs, checkpointId),
|
|
107
|
+
[r.threadIdField]: threadId,
|
|
108
|
+
[r.checkpointNamespaceField]: storedCheckpointNs,
|
|
109
|
+
[r.checkpointIdField]: checkpointId,
|
|
110
|
+
[r.parentCheckpointIdField]: parentCheckpointId,
|
|
111
|
+
[r.rowKindField]: "checkpoint",
|
|
112
|
+
[r.taskIdField]: null,
|
|
113
|
+
[r.sequenceField]: 0,
|
|
114
|
+
[r.createdAtField]: createdAt,
|
|
115
|
+
[r.checkpointPayloadField]: this.serialize(checkpoint),
|
|
116
|
+
[r.metadataPayloadField]: this.serialize(metadata),
|
|
117
|
+
[r.writesPayloadField]: null,
|
|
118
|
+
[r.schemaVersionField]: 1,
|
|
119
|
+
});
|
|
120
|
+
return this.buildConfig(threadId, checkpointNs, checkpointId);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
putWrites(config, writes, taskId) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const r = this.resourceConfig;
|
|
126
|
+
const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
|
|
127
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
128
|
+
if (!checkpointId) {
|
|
129
|
+
throw new Error("putWrites requires checkpoint_id in config");
|
|
130
|
+
}
|
|
131
|
+
const createdAt = this.now();
|
|
132
|
+
yield Promise.all(writes.map((_a, index_1) => __awaiter(this, [_a, index_1], void 0, function* ([channel, value], index) {
|
|
133
|
+
const writeIndex = this.getWriteIndex(channel, index);
|
|
134
|
+
try {
|
|
135
|
+
yield this.resource().create({
|
|
136
|
+
[r.idField]: this.buildWritesRowId(threadId, storedCheckpointNs, checkpointId, taskId, writeIndex),
|
|
137
|
+
[r.threadIdField]: threadId,
|
|
138
|
+
[r.checkpointNamespaceField]: storedCheckpointNs,
|
|
139
|
+
[r.checkpointIdField]: checkpointId,
|
|
140
|
+
[r.parentCheckpointIdField]: null,
|
|
141
|
+
[r.rowKindField]: "writes",
|
|
142
|
+
[r.taskIdField]: taskId,
|
|
143
|
+
[r.sequenceField]: writeIndex,
|
|
144
|
+
[r.createdAtField]: createdAt,
|
|
145
|
+
[r.checkpointPayloadField]: null,
|
|
146
|
+
[r.metadataPayloadField]: null,
|
|
147
|
+
[r.writesPayloadField]: this.serialize([channel, value]),
|
|
148
|
+
[r.schemaVersionField]: 1,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (!this.isDuplicateCheckpointWriteError(error)) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
})));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
getTuple(config) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
var _a;
|
|
162
|
+
const r = this.resourceConfig;
|
|
163
|
+
const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
|
|
164
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
165
|
+
const checkpointRows = yield this.resource().list(checkpointId
|
|
166
|
+
? Filters.AND(Filters.EQ(r.threadIdField, threadId), Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs), Filters.EQ(r.checkpointIdField, checkpointId), Filters.EQ(r.rowKindField, "checkpoint"))
|
|
167
|
+
: Filters.AND(Filters.EQ(r.threadIdField, threadId), Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs), Filters.EQ(r.rowKindField, "checkpoint")), 1, undefined, [{ field: r.checkpointIdField, direction: "desc" }]);
|
|
168
|
+
const checkpointRow = checkpointRows[0];
|
|
169
|
+
if (!checkpointRow) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
const resolvedCheckpointId = String(checkpointRow[r.checkpointIdField]);
|
|
173
|
+
const writesRows = yield this.resource().list(Filters.AND(Filters.EQ(r.threadIdField, threadId), Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs), Filters.EQ(r.checkpointIdField, resolvedCheckpointId), Filters.EQ(r.rowKindField, "writes")), undefined, undefined, [{ field: r.sequenceField, direction: "asc" }]);
|
|
174
|
+
const pendingWrites = writesRows.flatMap((row) => {
|
|
175
|
+
var _a;
|
|
176
|
+
const taskId = String((_a = row[r.taskIdField]) !== null && _a !== void 0 ? _a : "");
|
|
177
|
+
const write = this.deserialize(row[r.writesPayloadField]);
|
|
178
|
+
if (!write) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
const [channel, value] = write;
|
|
182
|
+
return [[taskId, channel, value]];
|
|
183
|
+
});
|
|
184
|
+
const parentCheckpointId = checkpointRow[r.parentCheckpointIdField]
|
|
185
|
+
? String(checkpointRow[r.parentCheckpointIdField])
|
|
186
|
+
: null;
|
|
187
|
+
const tuple = {
|
|
188
|
+
config: this.buildConfig(threadId, checkpointNs, resolvedCheckpointId),
|
|
189
|
+
checkpoint: this.deserialize(checkpointRow[r.checkpointPayloadField]),
|
|
190
|
+
metadata: ((_a = this.deserialize(checkpointRow[r.metadataPayloadField])) !== null && _a !== void 0 ? _a : {}),
|
|
191
|
+
parentConfig: parentCheckpointId
|
|
192
|
+
? this.buildConfig(threadId, checkpointNs, parentCheckpointId)
|
|
193
|
+
: undefined,
|
|
194
|
+
pendingWrites,
|
|
195
|
+
};
|
|
196
|
+
return tuple;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
list(config, options) {
|
|
200
|
+
return __asyncGenerator(this, arguments, function* list_1() {
|
|
201
|
+
const r = this.resourceConfig;
|
|
202
|
+
const { threadId, checkpointNs } = this.getConfigValues(config);
|
|
203
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
204
|
+
const beforeCheckpointId = (options === null || options === void 0 ? void 0 : options.before)
|
|
205
|
+
? this.getConfigValues(options.before).checkpointId
|
|
206
|
+
: null;
|
|
207
|
+
const filters = [
|
|
208
|
+
Filters.EQ(r.rowKindField, "checkpoint"),
|
|
209
|
+
Filters.EQ(r.threadIdField, threadId),
|
|
210
|
+
Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
|
|
211
|
+
];
|
|
212
|
+
if (beforeCheckpointId) {
|
|
213
|
+
filters.push(Filters.LT(r.checkpointIdField, beforeCheckpointId));
|
|
214
|
+
}
|
|
215
|
+
const rows = yield __await(this.resource().list(Filters.AND(...filters), options === null || options === void 0 ? void 0 : options.limit, undefined, [{ field: r.checkpointIdField, direction: "desc" }]));
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
const tuple = yield __await(this.getTuple(this.buildConfig(String(row[r.threadIdField]), this.decodeCheckpointNamespace(row[r.checkpointNamespaceField]), String(row[r.checkpointIdField]))));
|
|
218
|
+
if (tuple) {
|
|
219
|
+
yield yield __await(tuple);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
deleteThread(threadId_1) {
|
|
225
|
+
return __awaiter(this, arguments, void 0, function* (threadId, checkpointNs = "") {
|
|
226
|
+
const r = this.resourceConfig;
|
|
227
|
+
const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
|
|
228
|
+
const rows = yield this.resource().list(Filters.AND(Filters.EQ(r.threadIdField, threadId), Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs)), undefined, undefined, [{ field: r.createdAtField, direction: "desc" }]);
|
|
229
|
+
for (const row of rows) {
|
|
230
|
+
yield this.adminforth
|
|
231
|
+
.resource(this.pluginOptions.checkpointResource.resourceId)
|
|
232
|
+
.delete(row[r.idField]);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -10,14 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { createAgent, summarizationMiddleware } from "langchain";
|
|
11
11
|
import { logger } from "adminforth";
|
|
12
12
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
13
|
-
import { MemorySaver } from "@langchain/langgraph";
|
|
14
13
|
import { z } from "zod";
|
|
15
14
|
import { ChatOpenAI } from "@langchain/openai";
|
|
16
15
|
import { createAgentTools } from "./tools/index.js";
|
|
17
16
|
import { createApiBasedToolsMiddleware } from "./middleware/apiBasedTools.js";
|
|
18
17
|
import { createSequenceDebugMiddleware, } from "./middleware/sequenceDebug.js";
|
|
19
18
|
import { createOpenAiResponsesContinuationMiddleware } from "./middleware/openAiResponsesContinuation.js";
|
|
20
|
-
const checkpointer = new MemorySaver();
|
|
21
19
|
export const contextSchema = z.object({
|
|
22
20
|
adminUser: z.custom(),
|
|
23
21
|
userTimeZone: z.string(),
|
|
@@ -113,8 +111,10 @@ export function createAgentChatModel(params) {
|
|
|
113
111
|
const model = (_c = (_b = params.modelName) !== null && _b !== void 0 ? _b : options.model) !== null && _c !== void 0 ? _c : "gpt-5-nano";
|
|
114
112
|
const baseURL = (_d = options.baseURL) !== null && _d !== void 0 ? _d : options.baseUrl;
|
|
115
113
|
const reasoning = (_e = options.extraRequestBodyParameters) === null || _e === void 0 ? void 0 : _e.reasoning;
|
|
114
|
+
const reasoningConfig = reasoning
|
|
115
|
+
? Object.assign(Object.assign({}, reasoning), { summary: "auto" }) : undefined;
|
|
116
116
|
// @ts-ignore
|
|
117
|
-
return new ChatOpenAI(Object.assign(Object.assign(Object.assign({ apiKey: options.openAiApiKey, model, maxTokens: params.maxTokens, useResponsesApi: true, outputVersion: "v1", streaming: true, promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`, promptCacheRetention: "in_memory" }, (
|
|
117
|
+
return new ChatOpenAI(Object.assign(Object.assign(Object.assign({ apiKey: options.openAiApiKey, model, maxTokens: params.maxTokens, useResponsesApi: true, outputVersion: "v1", streaming: true, promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`, promptCacheRetention: "in_memory" }, (reasoningConfig ? { reasoning: reasoningConfig } : {})), (typeof options.timeoutMs === "number"
|
|
118
118
|
? { timeout: options.timeoutMs }
|
|
119
119
|
: {})), (baseURL
|
|
120
120
|
? {
|
|
@@ -126,7 +126,7 @@ export function createAgentChatModel(params) {
|
|
|
126
126
|
}
|
|
127
127
|
export function callAgent(params) {
|
|
128
128
|
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
-
const { name, model, summaryModel, messages, adminUser, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
|
|
129
|
+
const { name, model, summaryModel, checkpointer, messages, adminUser, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
|
|
130
130
|
const tools = yield createAgentTools(customComponentsDir, apiBasedTools);
|
|
131
131
|
const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools);
|
|
132
132
|
const openAiResponsesContinuationMiddleware = createOpenAiResponsesContinuationMiddleware();
|
|
@@ -193,6 +193,7 @@ const props = defineProps<{
|
|
|
193
193
|
name: string;
|
|
194
194
|
}>;
|
|
195
195
|
defaultModeName: string | null;
|
|
196
|
+
stickByDefault: boolean;
|
|
196
197
|
}
|
|
197
198
|
}>();
|
|
198
199
|
|
|
@@ -245,6 +246,7 @@ onMounted(async () => {
|
|
|
245
246
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
246
247
|
agentStore.regisrerTextInput(textInput.value);
|
|
247
248
|
textInput.value?.focus();
|
|
249
|
+
agentStore.setIsTeleportedToBody(props.meta.stickByDefault);
|
|
248
250
|
await agentStore.fetchSessionsList();
|
|
249
251
|
});
|
|
250
252
|
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,9 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
17
17
|
import { AdminForthPlugin, logger, Filters, Sorts } from "adminforth";
|
|
18
18
|
import { randomUUID } from 'crypto';
|
|
19
19
|
import { HumanMessage, SystemMessage } from "langchain";
|
|
20
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
20
21
|
import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
|
|
22
|
+
import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
|
|
21
23
|
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
22
24
|
import { prepareApiBasedTools as buildApiBasedTools, } from './apiBasedTools.js';
|
|
23
25
|
import { appendCustomSystemPrompt, buildAgentSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT, } from "./agent/systemPrompt.js";
|
|
@@ -88,9 +90,38 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
88
90
|
}));
|
|
89
91
|
});
|
|
90
92
|
}
|
|
93
|
+
getModeModels(mode, maxTokens) {
|
|
94
|
+
const cachedModels = this.modelsByModeName.get(mode.name);
|
|
95
|
+
if (cachedModels) {
|
|
96
|
+
return cachedModels;
|
|
97
|
+
}
|
|
98
|
+
const models = {
|
|
99
|
+
model: createAgentChatModel({
|
|
100
|
+
adapter: mode.completionAdapter,
|
|
101
|
+
maxTokens,
|
|
102
|
+
}),
|
|
103
|
+
summaryModel: createAgentChatModel({
|
|
104
|
+
adapter: mode.completionAdapter,
|
|
105
|
+
maxTokens,
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
this.modelsByModeName.set(mode.name, models);
|
|
109
|
+
return models;
|
|
110
|
+
}
|
|
111
|
+
getCheckpointer() {
|
|
112
|
+
if (this.checkpointer) {
|
|
113
|
+
return this.checkpointer;
|
|
114
|
+
}
|
|
115
|
+
this.checkpointer = this.options.checkpointResource
|
|
116
|
+
? new AdminForthCheckpointSaver(this.adminforth, this.options)
|
|
117
|
+
: new MemorySaver();
|
|
118
|
+
return this.checkpointer;
|
|
119
|
+
}
|
|
91
120
|
constructor(options) {
|
|
92
121
|
super(options, import.meta.url);
|
|
93
122
|
this.apiBasedTools = {};
|
|
123
|
+
this.checkpointer = null;
|
|
124
|
+
this.modelsByModeName = new Map();
|
|
94
125
|
this.options = options;
|
|
95
126
|
this.agentSystemPromptPromise = Promise.resolve(appendCustomSystemPrompt(DEFAULT_AGENT_SYSTEM_PROMPT, this.options.systemPrompt));
|
|
96
127
|
this.shouldHaveSingleInstancePerWholeApp = () => false;
|
|
@@ -100,7 +131,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
100
131
|
modifyResourceConfig: { get: () => super.modifyResourceConfig }
|
|
101
132
|
});
|
|
102
133
|
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
-
var _a;
|
|
134
|
+
var _a, _b;
|
|
104
135
|
_super.modifyResourceConfig.call(this, adminforth, resourceConfig);
|
|
105
136
|
if (!((_a = this.options.modes) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
106
137
|
throw new Error("modes is required for AdminForthAgentPlugin");
|
|
@@ -114,6 +145,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
114
145
|
pluginInstanceId: this.pluginInstanceId,
|
|
115
146
|
modes: this.options.modes.map((mode) => ({ name: mode.name })),
|
|
116
147
|
defaultModeName: this.options.modes[0].name,
|
|
148
|
+
stickByDefault: (_b = this.options.stickByDefault) !== null && _b !== void 0 ? _b : false,
|
|
117
149
|
}
|
|
118
150
|
});
|
|
119
151
|
if (!this.pluginOptions.sessionResource) {
|
|
@@ -240,19 +272,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
240
272
|
});
|
|
241
273
|
const maxTokens = (_f = this.options.maxTokens) !== null && _f !== void 0 ? _f : 10000;
|
|
242
274
|
const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
|
|
243
|
-
const model =
|
|
244
|
-
adapter: selectedMode.completionAdapter,
|
|
245
|
-
maxTokens,
|
|
246
|
-
});
|
|
247
|
-
const summaryModel = createAgentChatModel({
|
|
248
|
-
adapter: selectedMode.completionAdapter,
|
|
249
|
-
maxTokens,
|
|
250
|
-
});
|
|
275
|
+
const { model, summaryModel } = this.getModeModels(selectedMode, maxTokens);
|
|
251
276
|
const systemPrompt = yield this.agentSystemPromptPromise;
|
|
252
277
|
const stream = yield callAgent({
|
|
253
278
|
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
254
279
|
model,
|
|
255
280
|
summaryModel,
|
|
281
|
+
checkpointer: this.getCheckpointer(),
|
|
256
282
|
messages: [
|
|
257
283
|
new SystemMessage(systemPrompt),
|
|
258
284
|
new HumanMessage(prompt),
|
package/index.ts
CHANGED
|
@@ -9,7 +9,9 @@ import { AdminForthPlugin, logger, Filters, Sorts } from "adminforth";
|
|
|
9
9
|
import type { PluginOptions } from './types.js';
|
|
10
10
|
import { randomUUID } from 'crypto';
|
|
11
11
|
import { HumanMessage, SystemMessage } from "langchain";
|
|
12
|
+
import { MemorySaver, type BaseCheckpointSaver } from "@langchain/langgraph";
|
|
12
13
|
import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
|
|
14
|
+
import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
|
|
13
15
|
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
14
16
|
import {
|
|
15
17
|
prepareApiBasedTools as buildApiBasedTools,
|
|
@@ -22,6 +24,7 @@ import {
|
|
|
22
24
|
} from "./agent/systemPrompt.js";
|
|
23
25
|
import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "./agent/tools/index.js";
|
|
24
26
|
import type { ToolCallEvent } from "./agent/toolCallEvents.js";
|
|
27
|
+
import type { ChatOpenAI } from "@langchain/openai";
|
|
25
28
|
|
|
26
29
|
function isAggregateErrorLike(
|
|
27
30
|
error: unknown,
|
|
@@ -69,6 +72,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
69
72
|
options: PluginOptions;
|
|
70
73
|
apiBasedTools: Record<string, ApiBasedTool> = {};
|
|
71
74
|
agentSystemPromptPromise: Promise<string>;
|
|
75
|
+
private checkpointer: BaseCheckpointSaver | null = null;
|
|
76
|
+
private readonly modelsByModeName = new Map<
|
|
77
|
+
string,
|
|
78
|
+
{
|
|
79
|
+
model: ChatOpenAI;
|
|
80
|
+
summaryModel: ChatOpenAI;
|
|
81
|
+
}
|
|
82
|
+
>();
|
|
72
83
|
|
|
73
84
|
private async createNewTurn(sessionId: string, prompt: string, response?: string) {
|
|
74
85
|
const turnId = randomUUID();
|
|
@@ -107,6 +118,43 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
107
118
|
}));
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
private getModeModels(
|
|
122
|
+
mode: PluginOptions["modes"][number],
|
|
123
|
+
maxTokens: number,
|
|
124
|
+
) {
|
|
125
|
+
const cachedModels = this.modelsByModeName.get(mode.name);
|
|
126
|
+
|
|
127
|
+
if (cachedModels) {
|
|
128
|
+
return cachedModels;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const models = {
|
|
132
|
+
model: createAgentChatModel({
|
|
133
|
+
adapter: mode.completionAdapter,
|
|
134
|
+
maxTokens,
|
|
135
|
+
}),
|
|
136
|
+
summaryModel: createAgentChatModel({
|
|
137
|
+
adapter: mode.completionAdapter,
|
|
138
|
+
maxTokens,
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
this.modelsByModeName.set(mode.name, models);
|
|
143
|
+
return models;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private getCheckpointer() {
|
|
147
|
+
if (this.checkpointer) {
|
|
148
|
+
return this.checkpointer;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.checkpointer = this.options.checkpointResource
|
|
152
|
+
? new AdminForthCheckpointSaver(this.adminforth, this.options)
|
|
153
|
+
: new MemorySaver();
|
|
154
|
+
|
|
155
|
+
return this.checkpointer;
|
|
156
|
+
}
|
|
157
|
+
|
|
110
158
|
constructor(options: PluginOptions) {
|
|
111
159
|
super(options, import.meta.url);
|
|
112
160
|
this.options = options;
|
|
@@ -130,6 +178,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
130
178
|
pluginInstanceId: this.pluginInstanceId,
|
|
131
179
|
modes: this.options.modes.map((mode) => ({ name: mode.name })),
|
|
132
180
|
defaultModeName: this.options.modes[0].name,
|
|
181
|
+
stickByDefault: this.options.stickByDefault ?? false,
|
|
133
182
|
}
|
|
134
183
|
});
|
|
135
184
|
if (!this.pluginOptions.sessionResource) {
|
|
@@ -277,20 +326,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
277
326
|
|
|
278
327
|
const maxTokens = this.options.maxTokens ?? 10000;
|
|
279
328
|
const selectedMode = this.options.modes.find((mode) => mode.name === body.mode) ?? this.options.modes[0];
|
|
280
|
-
const model =
|
|
281
|
-
adapter: selectedMode.completionAdapter,
|
|
282
|
-
maxTokens,
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
const summaryModel = createAgentChatModel({
|
|
286
|
-
adapter: selectedMode.completionAdapter,
|
|
287
|
-
maxTokens,
|
|
288
|
-
});
|
|
329
|
+
const { model, summaryModel } = this.getModeModels(selectedMode, maxTokens);
|
|
289
330
|
const systemPrompt = await this.agentSystemPromptPromise;
|
|
290
331
|
const stream = await callAgent({
|
|
291
332
|
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
292
333
|
model,
|
|
293
334
|
summaryModel,
|
|
335
|
+
checkpointer: this.getCheckpointer(),
|
|
294
336
|
messages: [
|
|
295
337
|
new SystemMessage(systemPrompt),
|
|
296
338
|
new HumanMessage(prompt),
|
|
@@ -322,7 +364,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
322
364
|
: Array.isArray(token?.content)
|
|
323
365
|
? token.content
|
|
324
366
|
: [];
|
|
325
|
-
|
|
326
367
|
const reasoningDelta = blocks
|
|
327
368
|
.filter((b: any) => b?.type === "reasoning")
|
|
328
369
|
.map((b: any) => String(b.reasoning ?? ""))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@langchain/core": "^1.1.40",
|
|
25
25
|
"@langchain/langgraph": "^1.2.8",
|
|
26
|
+
"@langchain/langgraph-checkpoint": "^1.0.1",
|
|
26
27
|
"@langchain/openai": "^1.4.4",
|
|
27
28
|
"adminforth": "2.27.0-next.47",
|
|
28
29
|
"dayjs": "^1.11.20",
|
package/types.ts
CHANGED
|
@@ -24,6 +24,23 @@ interface ITurnResource {
|
|
|
24
24
|
debugField?: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
interface ICheckpointResource {
|
|
28
|
+
resourceId: string;
|
|
29
|
+
idField: string;
|
|
30
|
+
threadIdField: string;
|
|
31
|
+
checkpointNamespaceField: string;
|
|
32
|
+
checkpointIdField: string;
|
|
33
|
+
parentCheckpointIdField: string;
|
|
34
|
+
rowKindField: string;
|
|
35
|
+
taskIdField: string;
|
|
36
|
+
sequenceField: string;
|
|
37
|
+
createdAtField: string;
|
|
38
|
+
checkpointPayloadField: string;
|
|
39
|
+
metadataPayloadField: string;
|
|
40
|
+
writesPayloadField: string;
|
|
41
|
+
schemaVersionField: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
export interface PluginOptions extends PluginsCommonOptions {
|
|
28
45
|
/**
|
|
29
46
|
* Optional placeholder examples to preload for the chat textarea.
|
|
@@ -70,4 +87,15 @@ export interface PluginOptions extends PluginsCommonOptions {
|
|
|
70
87
|
* Resource configuration for turns.
|
|
71
88
|
*/
|
|
72
89
|
turnResource: ITurnResource;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Makes chat sticky by default. By default this prop is false
|
|
93
|
+
*/
|
|
94
|
+
stickByDefault?: boolean;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Optional resource configuration for a persistent LangGraph checkpointer.
|
|
98
|
+
* Falls back to an in-memory MemorySaver when omitted.
|
|
99
|
+
*/
|
|
100
|
+
checkpointResource?: ICheckpointResource;
|
|
73
101
|
}
|