@adminforth/agent 1.17.0 → 1.18.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/agent/checkpointer.ts +328 -0
- package/agent/simpleAgent.ts +14 -5
- package/build.log +3 -2
- package/custom/ChatSurface.vue +8 -5
- package/custom/ConversationArea.vue +1 -1
- package/custom/composables/useAgentStore.ts +17 -11
- package/custom/utils.ts +9 -0
- package/dist/agent/checkpointer.js +236 -0
- package/dist/agent/simpleAgent.js +4 -4
- package/dist/custom/ChatSurface.vue +8 -5
- package/dist/custom/ConversationArea.vue +1 -1
- package/dist/custom/composables/useAgentStore.ts +17 -11
- package/dist/custom/utils.ts +9 -0
- package/dist/index.js +33 -8
- package/index.ts +50 -10
- package/package.json +2 -1
- package/types.ts +23 -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
|
@@ -15,6 +15,7 @@ custom/package.json
|
|
|
15
15
|
custom/pnpm-lock.yaml
|
|
16
16
|
custom/tsconfig.json
|
|
17
17
|
custom/types.ts
|
|
18
|
+
custom/utils.ts
|
|
18
19
|
custom/composables/
|
|
19
20
|
custom/composables/useAgentStore.ts
|
|
20
21
|
custom/composables/useAgentTransitions.ts
|
|
@@ -31,5 +32,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
31
32
|
custom/skills/mutate_data/
|
|
32
33
|
custom/skills/mutate_data/SKILL.md
|
|
33
34
|
|
|
34
|
-
sent
|
|
35
|
-
total size is
|
|
35
|
+
sent 186,137 bytes received 455 bytes 373,184.00 bytes/sec
|
|
36
|
+
total size is 184,285 speedup is 0.99
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
24
24
|
flex flex z-40"
|
|
25
25
|
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
26
|
-
:style="{ width: agentStore.chatWidth + '
|
|
26
|
+
:style="{ width: agentStore.chatWidth + 'rem' }"
|
|
27
27
|
>
|
|
28
28
|
<div
|
|
29
29
|
v-if="!coreStore.isMobile"
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
<div
|
|
104
104
|
class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
|
|
105
105
|
:style="{
|
|
106
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%',
|
|
106
|
+
maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
|
|
107
107
|
transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
108
108
|
}"
|
|
109
109
|
>
|
|
@@ -185,6 +185,7 @@ import { useAgentStore } from './composables/useAgentStore';
|
|
|
185
185
|
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
186
186
|
import { Button } from '@/afcl';
|
|
187
187
|
import { useCoreStore } from '@/stores/core';
|
|
188
|
+
import { remToPx, pxToRem } from './utils';
|
|
188
189
|
|
|
189
190
|
const props = defineProps<{
|
|
190
191
|
meta: {
|
|
@@ -209,7 +210,7 @@ let startWidth = 0
|
|
|
209
210
|
|
|
210
211
|
const startResize = (e: MouseEvent) => {
|
|
211
212
|
startX = e.clientX
|
|
212
|
-
startWidth = agentStore.chatWidth
|
|
213
|
+
startWidth = remToPx(agentStore.chatWidth)
|
|
213
214
|
|
|
214
215
|
document.body.style.userSelect = 'none'
|
|
215
216
|
document.body.style.cursor = 'ew-resize'
|
|
@@ -220,7 +221,7 @@ const startResize = (e: MouseEvent) => {
|
|
|
220
221
|
|
|
221
222
|
const onResize = (e: MouseEvent) => {
|
|
222
223
|
const dx = startX - e.clientX
|
|
223
|
-
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, agentStore.MIN_WIDTH), agentStore.MAX_WIDTH))
|
|
224
|
+
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, remToPx(agentStore.MIN_WIDTH)), remToPx(agentStore.MAX_WIDTH)))
|
|
224
225
|
agentTransitions.setChatSurfaceTransition(true);
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -246,7 +247,9 @@ onMounted(async () => {
|
|
|
246
247
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
247
248
|
agentStore.regisrerTextInput(textInput.value);
|
|
248
249
|
textInput.value?.focus();
|
|
249
|
-
agentStore.
|
|
250
|
+
const isTeleportedToBodyFromLocalStorage = agentStore.getLocalStorageItem('isTeleportedToBody') === 'true';
|
|
251
|
+
|
|
252
|
+
agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
|
|
250
253
|
await agentStore.fetchSessionsList();
|
|
251
254
|
});
|
|
252
255
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
:threshold="10"
|
|
25
25
|
behavior="smooth"
|
|
26
26
|
:style="{
|
|
27
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'
|
|
27
|
+
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'rem' : '100%',
|
|
28
28
|
transition: `
|
|
29
29
|
max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out,
|
|
30
30
|
transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out
|
|
@@ -8,6 +8,7 @@ import { DefaultChatTransport } from 'ai';
|
|
|
8
8
|
import { useCoreStore } from '@/stores/core';
|
|
9
9
|
import { useAgentTransitions } from './useAgentTransitions';
|
|
10
10
|
import { useWindowSize } from '@vueuse/core';
|
|
11
|
+
import { remToPx, pxToRem } from '../utils';
|
|
11
12
|
|
|
12
13
|
type AgentMode = {
|
|
13
14
|
name: string;
|
|
@@ -19,9 +20,9 @@ const PLACEHOLDER_DELETING_DELAY_MS = 35;
|
|
|
19
20
|
const PLACEHOLDER_HOLD_DELAY_MS = 3000;
|
|
20
21
|
|
|
21
22
|
export const useAgentStore = defineStore('agent', () => {
|
|
22
|
-
const DEFAULT_CHAT_WIDTH =
|
|
23
|
-
const MAX_WIDTH =
|
|
24
|
-
const MIN_WIDTH =
|
|
23
|
+
const DEFAULT_CHAT_WIDTH = 30;
|
|
24
|
+
const MAX_WIDTH = 60;
|
|
25
|
+
const MIN_WIDTH = 25
|
|
25
26
|
const agentTransitions = useAgentTransitions();
|
|
26
27
|
|
|
27
28
|
const activeSessionId = ref<string | null>(null);
|
|
@@ -71,6 +72,9 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
71
72
|
})
|
|
72
73
|
watch(chatWidth, (newVal: number) => {
|
|
73
74
|
setLocalStorageItem('chatWidth', newVal.toString());
|
|
75
|
+
if (!isFullScreen.value) {
|
|
76
|
+
setLocalStorageItem('chatWidthBeforeFullScreen', newVal.toString());
|
|
77
|
+
}
|
|
74
78
|
})
|
|
75
79
|
watch(activeSessionId, (newVal: string | null) => {
|
|
76
80
|
if (newVal) {
|
|
@@ -90,14 +94,14 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
90
94
|
onMounted(() => {
|
|
91
95
|
const chatWidthBeforeFullScreen = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || '0', 10);
|
|
92
96
|
if (chatWidthBeforeFullScreen) {
|
|
93
|
-
|
|
97
|
+
setChatWidth(remToPx(chatWidthBeforeFullScreen));
|
|
94
98
|
} else {
|
|
95
99
|
const savedChatWidth = parseInt(getLocalStorageItem('chatWidth') || '0', 10);
|
|
96
100
|
if (savedChatWidth) {
|
|
97
101
|
if (savedChatWidth > MAX_WIDTH || savedChatWidth < MIN_WIDTH) {
|
|
98
|
-
|
|
102
|
+
setChatWidth(remToPx(DEFAULT_CHAT_WIDTH));
|
|
99
103
|
} else {
|
|
100
|
-
|
|
104
|
+
setChatWidth(remToPx(savedChatWidth));
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
}
|
|
@@ -110,7 +114,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
110
114
|
isChatOpen.value = getLocalStorageItem('isChatOpen') === 'true';
|
|
111
115
|
}
|
|
112
116
|
if (coreStore.isMobile) {
|
|
113
|
-
|
|
117
|
+
setChatWidth(window.innerWidth);
|
|
114
118
|
}
|
|
115
119
|
appRoot.value = document.getElementById('app');
|
|
116
120
|
header.value = document.getElementById('af-header-nav');
|
|
@@ -135,23 +139,24 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
135
139
|
const isTeleportedBeforeFullScreen = getLocalStorageItem('isTeleportedToBodyBeforeFullScreen') === 'true';
|
|
136
140
|
agentTransitions.setAppRootTransition(true);
|
|
137
141
|
setIsTeleportedToBody(isTeleportedBeforeFullScreen);
|
|
138
|
-
setChatWidth(lastChatWidth, false);
|
|
142
|
+
setChatWidth(remToPx(lastChatWidth), false);
|
|
139
143
|
setTimeout(() => agentTransitions.setAppRootTransition(false), agentTransitions.TRANSITION_DURATION);
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
|
|
147
|
+
//takes on input width in pixels, converts to rem and sets chat width
|
|
143
148
|
function setChatWidth(width: number, blockTransition = true) {
|
|
144
149
|
if (blockTransition) {
|
|
145
150
|
agentTransitions.setAppRootTransition(true);
|
|
146
151
|
}
|
|
147
|
-
chatWidth.value = width;
|
|
152
|
+
chatWidth.value = pxToRem(width);
|
|
148
153
|
|
|
149
154
|
}
|
|
150
155
|
watch([isTeleportedToBody, isChatOpen, chatWidth], ([newIsTeleportedToBody, newIsChatOpen, newChatWidth]: [boolean, boolean, number]) => {
|
|
151
156
|
if (appRoot.value && header.value) {
|
|
152
157
|
if (newIsTeleportedToBody && newIsChatOpen) {
|
|
153
|
-
appRoot.value.style.paddingRight = `${chatWidth.value}px`;
|
|
154
|
-
header.value.style.paddingRight = `${chatWidth.value}px`;
|
|
158
|
+
appRoot.value.style.paddingRight = `${remToPx(chatWidth.value)}px`;
|
|
159
|
+
header.value.style.paddingRight = `${remToPx(chatWidth.value)}px`;
|
|
155
160
|
} else {
|
|
156
161
|
appRoot.value.style.paddingRight = '';
|
|
157
162
|
header.value.style.paddingRight = '';
|
|
@@ -572,5 +577,6 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
572
577
|
DEFAULT_CHAT_WIDTH,
|
|
573
578
|
MAX_WIDTH,
|
|
574
579
|
MIN_WIDTH,
|
|
580
|
+
getLocalStorageItem
|
|
575
581
|
}
|
|
576
582
|
})
|
package/custom/utils.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function remToPx(rem: number): number {
|
|
2
|
+
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
3
|
+
return rem * rootFontSize;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function pxToRem(px: number): number {
|
|
7
|
+
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
8
|
+
return px / rootFontSize;
|
|
9
|
+
}
|
|
@@ -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();
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
class="fixed bg-lightNavbar dark:bg-darkNavbar h-screen top-0 right-0 border-x border-b border-gray-200 dark:border-gray-700
|
|
24
24
|
flex flex z-40"
|
|
25
25
|
:class="[agentStore.isChatOpen ? 'translate-x-0' : 'translate-x-full', !agentStore.isTeleportedToBody ? 'shadow-2xl' : '']"
|
|
26
|
-
:style="{ width: agentStore.chatWidth + '
|
|
26
|
+
:style="{ width: agentStore.chatWidth + 'rem' }"
|
|
27
27
|
>
|
|
28
28
|
<div
|
|
29
29
|
v-if="!coreStore.isMobile"
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
<div
|
|
104
104
|
class="w-full mb-2 flex items-center justify-center px-2 bg-transparent relative translate-x-[-50%] left-1/2"
|
|
105
105
|
:style="{
|
|
106
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'px' : '100%',
|
|
106
|
+
maxWidth: agentStore.isFullScreen ? remToPx(agentStore.MAX_WIDTH)+'px' : '100%',
|
|
107
107
|
transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
108
108
|
}"
|
|
109
109
|
>
|
|
@@ -185,6 +185,7 @@ import { useAgentStore } from './composables/useAgentStore';
|
|
|
185
185
|
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
186
186
|
import { Button } from '@/afcl';
|
|
187
187
|
import { useCoreStore } from '@/stores/core';
|
|
188
|
+
import { remToPx, pxToRem } from './utils';
|
|
188
189
|
|
|
189
190
|
const props = defineProps<{
|
|
190
191
|
meta: {
|
|
@@ -209,7 +210,7 @@ let startWidth = 0
|
|
|
209
210
|
|
|
210
211
|
const startResize = (e: MouseEvent) => {
|
|
211
212
|
startX = e.clientX
|
|
212
|
-
startWidth = agentStore.chatWidth
|
|
213
|
+
startWidth = remToPx(agentStore.chatWidth)
|
|
213
214
|
|
|
214
215
|
document.body.style.userSelect = 'none'
|
|
215
216
|
document.body.style.cursor = 'ew-resize'
|
|
@@ -220,7 +221,7 @@ const startResize = (e: MouseEvent) => {
|
|
|
220
221
|
|
|
221
222
|
const onResize = (e: MouseEvent) => {
|
|
222
223
|
const dx = startX - e.clientX
|
|
223
|
-
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, agentStore.MIN_WIDTH), agentStore.MAX_WIDTH))
|
|
224
|
+
agentStore.setChatWidth(Math.min(Math.max(startWidth + dx, remToPx(agentStore.MIN_WIDTH)), remToPx(agentStore.MAX_WIDTH)))
|
|
224
225
|
agentTransitions.setChatSurfaceTransition(true);
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -246,7 +247,9 @@ onMounted(async () => {
|
|
|
246
247
|
agentStore.setAvailableModes(props.meta.modes, props.meta.defaultModeName);
|
|
247
248
|
agentStore.regisrerTextInput(textInput.value);
|
|
248
249
|
textInput.value?.focus();
|
|
249
|
-
agentStore.
|
|
250
|
+
const isTeleportedToBodyFromLocalStorage = agentStore.getLocalStorageItem('isTeleportedToBody') === 'true';
|
|
251
|
+
|
|
252
|
+
agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
|
|
250
253
|
await agentStore.fetchSessionsList();
|
|
251
254
|
});
|
|
252
255
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
:threshold="10"
|
|
25
25
|
behavior="smooth"
|
|
26
26
|
:style="{
|
|
27
|
-
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'
|
|
27
|
+
maxWidth: agentStore.isFullScreen ? agentStore.MAX_WIDTH+'rem' : '100%',
|
|
28
28
|
transition: `
|
|
29
29
|
max-width ${agentTransitions.TRANSITION_DURATION}ms ease-in-out,
|
|
30
30
|
transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out
|
|
@@ -8,6 +8,7 @@ import { DefaultChatTransport } from 'ai';
|
|
|
8
8
|
import { useCoreStore } from '@/stores/core';
|
|
9
9
|
import { useAgentTransitions } from './useAgentTransitions';
|
|
10
10
|
import { useWindowSize } from '@vueuse/core';
|
|
11
|
+
import { remToPx, pxToRem } from '../utils';
|
|
11
12
|
|
|
12
13
|
type AgentMode = {
|
|
13
14
|
name: string;
|
|
@@ -19,9 +20,9 @@ const PLACEHOLDER_DELETING_DELAY_MS = 35;
|
|
|
19
20
|
const PLACEHOLDER_HOLD_DELAY_MS = 3000;
|
|
20
21
|
|
|
21
22
|
export const useAgentStore = defineStore('agent', () => {
|
|
22
|
-
const DEFAULT_CHAT_WIDTH =
|
|
23
|
-
const MAX_WIDTH =
|
|
24
|
-
const MIN_WIDTH =
|
|
23
|
+
const DEFAULT_CHAT_WIDTH = 30;
|
|
24
|
+
const MAX_WIDTH = 60;
|
|
25
|
+
const MIN_WIDTH = 25
|
|
25
26
|
const agentTransitions = useAgentTransitions();
|
|
26
27
|
|
|
27
28
|
const activeSessionId = ref<string | null>(null);
|
|
@@ -71,6 +72,9 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
71
72
|
})
|
|
72
73
|
watch(chatWidth, (newVal: number) => {
|
|
73
74
|
setLocalStorageItem('chatWidth', newVal.toString());
|
|
75
|
+
if (!isFullScreen.value) {
|
|
76
|
+
setLocalStorageItem('chatWidthBeforeFullScreen', newVal.toString());
|
|
77
|
+
}
|
|
74
78
|
})
|
|
75
79
|
watch(activeSessionId, (newVal: string | null) => {
|
|
76
80
|
if (newVal) {
|
|
@@ -90,14 +94,14 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
90
94
|
onMounted(() => {
|
|
91
95
|
const chatWidthBeforeFullScreen = parseInt(getLocalStorageItem('chatWidthBeforeFullScreen') || '0', 10);
|
|
92
96
|
if (chatWidthBeforeFullScreen) {
|
|
93
|
-
|
|
97
|
+
setChatWidth(remToPx(chatWidthBeforeFullScreen));
|
|
94
98
|
} else {
|
|
95
99
|
const savedChatWidth = parseInt(getLocalStorageItem('chatWidth') || '0', 10);
|
|
96
100
|
if (savedChatWidth) {
|
|
97
101
|
if (savedChatWidth > MAX_WIDTH || savedChatWidth < MIN_WIDTH) {
|
|
98
|
-
|
|
102
|
+
setChatWidth(remToPx(DEFAULT_CHAT_WIDTH));
|
|
99
103
|
} else {
|
|
100
|
-
|
|
104
|
+
setChatWidth(remToPx(savedChatWidth));
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
}
|
|
@@ -110,7 +114,7 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
110
114
|
isChatOpen.value = getLocalStorageItem('isChatOpen') === 'true';
|
|
111
115
|
}
|
|
112
116
|
if (coreStore.isMobile) {
|
|
113
|
-
|
|
117
|
+
setChatWidth(window.innerWidth);
|
|
114
118
|
}
|
|
115
119
|
appRoot.value = document.getElementById('app');
|
|
116
120
|
header.value = document.getElementById('af-header-nav');
|
|
@@ -135,23 +139,24 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
135
139
|
const isTeleportedBeforeFullScreen = getLocalStorageItem('isTeleportedToBodyBeforeFullScreen') === 'true';
|
|
136
140
|
agentTransitions.setAppRootTransition(true);
|
|
137
141
|
setIsTeleportedToBody(isTeleportedBeforeFullScreen);
|
|
138
|
-
setChatWidth(lastChatWidth, false);
|
|
142
|
+
setChatWidth(remToPx(lastChatWidth), false);
|
|
139
143
|
setTimeout(() => agentTransitions.setAppRootTransition(false), agentTransitions.TRANSITION_DURATION);
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
|
|
147
|
+
//takes on input width in pixels, converts to rem and sets chat width
|
|
143
148
|
function setChatWidth(width: number, blockTransition = true) {
|
|
144
149
|
if (blockTransition) {
|
|
145
150
|
agentTransitions.setAppRootTransition(true);
|
|
146
151
|
}
|
|
147
|
-
chatWidth.value = width;
|
|
152
|
+
chatWidth.value = pxToRem(width);
|
|
148
153
|
|
|
149
154
|
}
|
|
150
155
|
watch([isTeleportedToBody, isChatOpen, chatWidth], ([newIsTeleportedToBody, newIsChatOpen, newChatWidth]: [boolean, boolean, number]) => {
|
|
151
156
|
if (appRoot.value && header.value) {
|
|
152
157
|
if (newIsTeleportedToBody && newIsChatOpen) {
|
|
153
|
-
appRoot.value.style.paddingRight = `${chatWidth.value}px`;
|
|
154
|
-
header.value.style.paddingRight = `${chatWidth.value}px`;
|
|
158
|
+
appRoot.value.style.paddingRight = `${remToPx(chatWidth.value)}px`;
|
|
159
|
+
header.value.style.paddingRight = `${remToPx(chatWidth.value)}px`;
|
|
155
160
|
} else {
|
|
156
161
|
appRoot.value.style.paddingRight = '';
|
|
157
162
|
header.value.style.paddingRight = '';
|
|
@@ -572,5 +577,6 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
572
577
|
DEFAULT_CHAT_WIDTH,
|
|
573
578
|
MAX_WIDTH,
|
|
574
579
|
MIN_WIDTH,
|
|
580
|
+
getLocalStorageItem
|
|
575
581
|
}
|
|
576
582
|
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function remToPx(rem: number): number {
|
|
2
|
+
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
3
|
+
return rem * rootFontSize;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function pxToRem(px: number): number {
|
|
7
|
+
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
8
|
+
return px / rootFontSize;
|
|
9
|
+
}
|
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;
|
|
@@ -241,19 +272,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
241
272
|
});
|
|
242
273
|
const maxTokens = (_f = this.options.maxTokens) !== null && _f !== void 0 ? _f : 10000;
|
|
243
274
|
const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
|
|
244
|
-
const model =
|
|
245
|
-
adapter: selectedMode.completionAdapter,
|
|
246
|
-
maxTokens,
|
|
247
|
-
});
|
|
248
|
-
const summaryModel = createAgentChatModel({
|
|
249
|
-
adapter: selectedMode.completionAdapter,
|
|
250
|
-
maxTokens,
|
|
251
|
-
});
|
|
275
|
+
const { model, summaryModel } = this.getModeModels(selectedMode, maxTokens);
|
|
252
276
|
const systemPrompt = yield this.agentSystemPromptPromise;
|
|
253
277
|
const stream = yield callAgent({
|
|
254
278
|
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
255
279
|
model,
|
|
256
280
|
summaryModel,
|
|
281
|
+
checkpointer: this.getCheckpointer(),
|
|
257
282
|
messages: [
|
|
258
283
|
new SystemMessage(systemPrompt),
|
|
259
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;
|
|
@@ -278,20 +326,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
278
326
|
|
|
279
327
|
const maxTokens = this.options.maxTokens ?? 10000;
|
|
280
328
|
const selectedMode = this.options.modes.find((mode) => mode.name === body.mode) ?? this.options.modes[0];
|
|
281
|
-
const model =
|
|
282
|
-
adapter: selectedMode.completionAdapter,
|
|
283
|
-
maxTokens,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const summaryModel = createAgentChatModel({
|
|
287
|
-
adapter: selectedMode.completionAdapter,
|
|
288
|
-
maxTokens,
|
|
289
|
-
});
|
|
329
|
+
const { model, summaryModel } = this.getModeModels(selectedMode, maxTokens);
|
|
290
330
|
const systemPrompt = await this.agentSystemPromptPromise;
|
|
291
331
|
const stream = await callAgent({
|
|
292
332
|
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
293
333
|
model,
|
|
294
334
|
summaryModel,
|
|
335
|
+
checkpointer: this.getCheckpointer(),
|
|
295
336
|
messages: [
|
|
296
337
|
new SystemMessage(systemPrompt),
|
|
297
338
|
new HumanMessage(prompt),
|
|
@@ -323,7 +364,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
323
364
|
: Array.isArray(token?.content)
|
|
324
365
|
? token.content
|
|
325
366
|
: [];
|
|
326
|
-
|
|
327
367
|
const reasoningDelta = blocks
|
|
328
368
|
.filter((b: any) => b?.type === "reasoning")
|
|
329
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.1",
|
|
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.
|
|
@@ -75,4 +92,10 @@ export interface PluginOptions extends PluginsCommonOptions {
|
|
|
75
92
|
* Makes chat sticky by default. By default this prop is false
|
|
76
93
|
*/
|
|
77
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;
|
|
78
101
|
}
|