@adminforth/agent 1.34.2 → 1.36.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/simpleAgent.ts +5 -0
- package/agent/tools/getUserLocation.ts +45 -0
- package/agent/tools/index.ts +2 -0
- package/build.log +9 -2
- package/custom/ChatSurface.vue +15 -0
- package/custom/composables/agentStore/constants.ts +12 -0
- package/custom/composables/agentStore/pageContext.ts +8 -0
- package/custom/composables/agentStore/useAgentChat.ts +69 -0
- package/custom/composables/agentStore/useAgentPlaceholder.ts +142 -0
- package/custom/composables/agentStore/useAgentSessions.ts +270 -0
- package/custom/composables/useAgentStore.ts +73 -393
- package/custom/conversation_area/ProcessingTimeline.vue +1 -1
- package/custom/env.d.ts +7 -0
- package/dist/agent/simpleAgent.js +3 -1
- package/dist/agent/tools/getUserLocation.js +31 -0
- package/dist/agent/tools/index.js +2 -0
- package/dist/custom/ChatSurface.vue +15 -0
- package/dist/custom/composables/agentStore/constants.ts +12 -0
- package/dist/custom/composables/agentStore/pageContext.ts +8 -0
- package/dist/custom/composables/agentStore/useAgentChat.ts +69 -0
- package/dist/custom/composables/agentStore/useAgentPlaceholder.ts +142 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +270 -0
- package/dist/custom/composables/useAgentStore.ts +73 -393
- package/dist/custom/conversation_area/ProcessingTimeline.vue +1 -1
- package/dist/custom/env.d.ts +7 -0
- package/dist/index.js +264 -105
- package/index.ts +295 -103
- package/package.json +2 -2
- package/types.ts +6 -0
package/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AdminUser,
|
|
3
3
|
AdminForthResource,
|
|
4
|
+
HttpExtra,
|
|
4
5
|
IAdminForth,
|
|
5
|
-
IHttpServer
|
|
6
|
+
IHttpServer,
|
|
7
|
+
TextToSpeechInput,
|
|
6
8
|
} from "adminforth";
|
|
7
9
|
|
|
8
10
|
import { AdminForthPlugin, logger, Filters, Sorts } from "adminforth";
|
|
@@ -33,16 +35,36 @@ import {
|
|
|
33
35
|
} from "./agent/systemPrompt.js";
|
|
34
36
|
import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "./agent/tools/index.js";
|
|
35
37
|
import type { ToolCallEvent } from "./agent/toolCallEvents.js";
|
|
38
|
+
import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
|
|
36
39
|
|
|
37
40
|
type CurrentPageRequestBody = {
|
|
38
41
|
currentPage?: CurrentPageContext;
|
|
39
42
|
};
|
|
40
43
|
|
|
41
|
-
type
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
type SpeechResponseRequestBody = CurrentPageRequestBody & {
|
|
45
|
+
audioBase64: string;
|
|
46
|
+
filename: string;
|
|
47
|
+
mimeType: string;
|
|
48
|
+
prompt?: string;
|
|
49
|
+
sessionId?: string | null;
|
|
50
|
+
mode?: string | null;
|
|
51
|
+
timeZone?: string;
|
|
52
|
+
tts?: Omit<TextToSpeechInput, "text">;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type AgentTurnRunInput = {
|
|
56
|
+
prompt: string;
|
|
57
|
+
sessionId: string;
|
|
58
|
+
turnId: string;
|
|
59
|
+
modeName?: string | null;
|
|
60
|
+
userTimeZone: string;
|
|
61
|
+
currentPage?: CurrentPageContext;
|
|
62
|
+
adminUser: AdminUser;
|
|
63
|
+
httpExtra: HttpExtra;
|
|
64
|
+
sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
|
|
65
|
+
emitReasoningDelta?: (delta: string) => void;
|
|
66
|
+
emitTextDelta?: (delta: string) => void;
|
|
67
|
+
emitToolCallEvent?: (event: ToolCallEvent) => void;
|
|
46
68
|
};
|
|
47
69
|
|
|
48
70
|
function isAggregateErrorLike(
|
|
@@ -73,6 +95,24 @@ function formatAgentError(error: unknown) {
|
|
|
73
95
|
return String(error);
|
|
74
96
|
}
|
|
75
97
|
|
|
98
|
+
function formatAgentResponseError(error: unknown): string {
|
|
99
|
+
if (isAggregateErrorLike(error)) {
|
|
100
|
+
const nestedErrors = error.errors.map(formatAgentResponseError);
|
|
101
|
+
|
|
102
|
+
if (nestedErrors.length) {
|
|
103
|
+
return nestedErrors.join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return error.message || "Agent response failed";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (error instanceof Error) {
|
|
110
|
+
return error.toString();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return String(error);
|
|
114
|
+
}
|
|
115
|
+
|
|
76
116
|
function formatAdminUserPrompt(adminUser: AdminUser, usernameField: string) {
|
|
77
117
|
const dbUser = adminUser.dbUser as Record<string, unknown>;
|
|
78
118
|
const adminUserContext = {
|
|
@@ -87,18 +127,6 @@ function formatAdminUserPrompt(adminUser: AdminUser, usernameField: string) {
|
|
|
87
127
|
].join("\n");
|
|
88
128
|
}
|
|
89
129
|
|
|
90
|
-
function formatCurrentPagePrompt(currentPage: CurrentPageContext | undefined) {
|
|
91
|
-
if (!currentPage) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return [
|
|
96
|
-
"Current user page context for the latest message:",
|
|
97
|
-
JSON.stringify(currentPage, null, 2),
|
|
98
|
-
"When the user says here, this page, current page, or opened page, treat it as this page.",
|
|
99
|
-
].join("\n");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
130
|
function assertRequiredApiTool(
|
|
103
131
|
apiBasedTools: Record<string, ApiBasedTool>,
|
|
104
132
|
toolName: string,
|
|
@@ -245,6 +273,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
245
273
|
modes: this.options.modes.map((mode) => ({ name: mode.name })),
|
|
246
274
|
defaultModeName: this.options.modes[0].name,
|
|
247
275
|
stickByDefault: this.options.stickByDefault ?? false,
|
|
276
|
+
hasAudioAdapter: Boolean(this.options.audioAdapter),
|
|
248
277
|
}
|
|
249
278
|
});
|
|
250
279
|
if (!this.pluginOptions.sessionResource) {
|
|
@@ -253,6 +282,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
253
282
|
}
|
|
254
283
|
|
|
255
284
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
|
|
285
|
+
this.options.audioAdapter?.validate();
|
|
256
286
|
this.agentSystemPromptPromise = buildAgentSystemPrompt(
|
|
257
287
|
adminforth,
|
|
258
288
|
this.getInternalAgentResourceIds(),
|
|
@@ -264,6 +294,101 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
264
294
|
return `single`;
|
|
265
295
|
}
|
|
266
296
|
|
|
297
|
+
private async runAgentTurn(input: AgentTurnRunInput) {
|
|
298
|
+
let fullResponse = "";
|
|
299
|
+
const maxTokens = this.options.maxTokens ?? 10000;
|
|
300
|
+
const selectedMode =
|
|
301
|
+
this.options.modes.find((mode) => mode.name === input.modeName) ??
|
|
302
|
+
this.options.modes[0];
|
|
303
|
+
const { model, summaryModel, modelMiddleware } =
|
|
304
|
+
await this.getModeModels(selectedMode, maxTokens);
|
|
305
|
+
const userLanguage = await detectUserLanguage(selectedMode.completionAdapter, input.prompt)
|
|
306
|
+
.catch((error) => {
|
|
307
|
+
logger.warn(`Failed to detect user language: ${error instanceof Error ? error.message : String(error)}`);
|
|
308
|
+
return null;
|
|
309
|
+
});
|
|
310
|
+
const systemPrompt = [
|
|
311
|
+
await this.agentSystemPromptPromise,
|
|
312
|
+
formatAdminUserPrompt(input.adminUser, this.adminforth.config.auth.usernameField),
|
|
313
|
+
formatLanguagePrompt(userLanguage),
|
|
314
|
+
].join("\n\n");
|
|
315
|
+
const apiBasedTools = buildApiBasedTools(
|
|
316
|
+
this.adminforth,
|
|
317
|
+
this.getInternalAgentResourceIds(),
|
|
318
|
+
);
|
|
319
|
+
for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
|
|
320
|
+
assertRequiredApiTool(apiBasedTools, toolName);
|
|
321
|
+
}
|
|
322
|
+
assertRequiredApiTool(apiBasedTools, "update_record");
|
|
323
|
+
this.apiBasedTools = apiBasedTools;
|
|
324
|
+
const stream = await callAgent({
|
|
325
|
+
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
326
|
+
model,
|
|
327
|
+
summaryModel,
|
|
328
|
+
modelMiddleware,
|
|
329
|
+
checkpointer: this.getCheckpointer(),
|
|
330
|
+
messages: [
|
|
331
|
+
new SystemMessage(systemPrompt),
|
|
332
|
+
new HumanMessage(input.prompt),
|
|
333
|
+
],
|
|
334
|
+
adminUser: input.adminUser,
|
|
335
|
+
adminforth: this.adminforth,
|
|
336
|
+
apiBasedTools,
|
|
337
|
+
customComponentsDir: this.adminforth.config.customization.customComponentsDir,
|
|
338
|
+
sessionId: input.sessionId,
|
|
339
|
+
turnId: input.turnId,
|
|
340
|
+
currentPage: input.currentPage,
|
|
341
|
+
httpExtra: input.httpExtra,
|
|
342
|
+
userTimeZone: input.userTimeZone,
|
|
343
|
+
emitToolCallEvent: (event) => {
|
|
344
|
+
input.sequenceDebugCollector.handleToolCallEvent(event);
|
|
345
|
+
input.emitToolCallEvent?.(event);
|
|
346
|
+
},
|
|
347
|
+
sequenceDebugSink: input.sequenceDebugCollector,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
for await (const rawChunk of stream as AsyncIterable<[any, any]>) {
|
|
351
|
+
const [token, metadata] = rawChunk;
|
|
352
|
+
|
|
353
|
+
const nodeName =
|
|
354
|
+
typeof metadata?.langgraph_node === "string"
|
|
355
|
+
? metadata.langgraph_node
|
|
356
|
+
: "";
|
|
357
|
+
|
|
358
|
+
if (nodeName && !["model", "model_request"].includes(nodeName)) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const blocks = Array.isArray(token?.contentBlocks)
|
|
363
|
+
? token.contentBlocks
|
|
364
|
+
: Array.isArray(token?.content)
|
|
365
|
+
? token.content
|
|
366
|
+
: [];
|
|
367
|
+
const reasoningDelta = blocks
|
|
368
|
+
.filter((b: any) => b?.type === "reasoning")
|
|
369
|
+
.map((b: any) => String(b.reasoning ?? ""))
|
|
370
|
+
.join("");
|
|
371
|
+
|
|
372
|
+
const textDelta = blocks
|
|
373
|
+
.filter((b: any) => b?.type === "text")
|
|
374
|
+
.map((b: any) => String(b.text ?? ""))
|
|
375
|
+
.join("");
|
|
376
|
+
|
|
377
|
+
if (reasoningDelta) {
|
|
378
|
+
input.emitReasoningDelta?.(reasoningDelta);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (textDelta) {
|
|
382
|
+
fullResponse += textDelta;
|
|
383
|
+
input.emitTextDelta?.(textDelta);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
text: fullResponse,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
267
392
|
setupEndpoints(server: IHttpServer) {
|
|
268
393
|
server.endpoint({
|
|
269
394
|
method: 'POST',
|
|
@@ -295,7 +420,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
295
420
|
server.endpoint({
|
|
296
421
|
method: 'POST',
|
|
297
422
|
path: `/agent/response`,
|
|
298
|
-
handler: async ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res }) => {
|
|
423
|
+
handler: async ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res, abortSignal }) => {
|
|
299
424
|
const res = _raw_express_res;
|
|
300
425
|
const messageId = randomUUID();
|
|
301
426
|
const prompt = body.message;
|
|
@@ -327,8 +452,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
327
452
|
endActiveBlock();
|
|
328
453
|
}
|
|
329
454
|
|
|
330
|
-
sequenceDebugCollector.handleToolCallEvent(event);
|
|
331
|
-
|
|
332
455
|
send({
|
|
333
456
|
type: "data-tool-call",
|
|
334
457
|
data: event,
|
|
@@ -389,47 +512,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
389
512
|
messageId,
|
|
390
513
|
});
|
|
391
514
|
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
const { model, summaryModel, modelMiddleware } =
|
|
395
|
-
await this.getModeModels(selectedMode, maxTokens);
|
|
396
|
-
const userLanguage = await detectUserLanguage(selectedMode.completionAdapter, prompt)
|
|
397
|
-
.catch((error) => {
|
|
398
|
-
logger.warn(`Failed to detect user language: ${error instanceof Error ? error.message : String(error)}`);
|
|
399
|
-
return null;
|
|
400
|
-
});
|
|
401
|
-
const systemPrompt = [
|
|
402
|
-
await this.agentSystemPromptPromise,
|
|
403
|
-
formatAdminUserPrompt(adminUser, this.adminforth.config.auth.usernameField),
|
|
404
|
-
formatLanguagePrompt(userLanguage),
|
|
405
|
-
].join("\n\n");
|
|
406
|
-
const apiBasedTools = buildApiBasedTools(
|
|
407
|
-
this.adminforth,
|
|
408
|
-
this.getInternalAgentResourceIds(),
|
|
409
|
-
);
|
|
410
|
-
for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
|
|
411
|
-
assertRequiredApiTool(apiBasedTools, toolName);
|
|
412
|
-
}
|
|
413
|
-
assertRequiredApiTool(apiBasedTools, "update_record");
|
|
414
|
-
this.apiBasedTools = apiBasedTools;
|
|
415
|
-
const currentPagePrompt = formatCurrentPagePrompt(currentPage);
|
|
416
|
-
const stream = await callAgent({
|
|
417
|
-
name: `adminforth-agent-${this.pluginInstanceId}`,
|
|
418
|
-
model,
|
|
419
|
-
summaryModel,
|
|
420
|
-
modelMiddleware,
|
|
421
|
-
checkpointer: this.getCheckpointer(),
|
|
422
|
-
messages: [
|
|
423
|
-
new SystemMessage(systemPrompt),
|
|
424
|
-
...(currentPagePrompt ? [new SystemMessage(currentPagePrompt)] : []),
|
|
425
|
-
new HumanMessage(prompt),
|
|
426
|
-
],
|
|
427
|
-
adminUser,
|
|
428
|
-
adminforth: this.adminforth,
|
|
429
|
-
apiBasedTools,
|
|
430
|
-
customComponentsDir: this.adminforth.config.customization.customComponentsDir,
|
|
515
|
+
const agentResponse = await this.runAgentTurn({
|
|
516
|
+
prompt,
|
|
431
517
|
sessionId,
|
|
432
518
|
turnId,
|
|
519
|
+
modeName: body.mode,
|
|
520
|
+
userTimeZone,
|
|
521
|
+
currentPage,
|
|
522
|
+
adminUser,
|
|
433
523
|
httpExtra: {
|
|
434
524
|
body,
|
|
435
525
|
query,
|
|
@@ -438,48 +528,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
438
528
|
requestUrl,
|
|
439
529
|
response,
|
|
440
530
|
},
|
|
441
|
-
|
|
531
|
+
sequenceDebugCollector,
|
|
442
532
|
emitToolCallEvent,
|
|
443
|
-
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
for await (const rawChunk of stream as AsyncIterable<[any, any]>) {
|
|
447
|
-
const [token, metadata] = rawChunk;
|
|
448
|
-
|
|
449
|
-
const nodeName =
|
|
450
|
-
typeof metadata?.langgraph_node === "string"
|
|
451
|
-
? metadata.langgraph_node
|
|
452
|
-
: "";
|
|
453
|
-
|
|
454
|
-
if (nodeName && !["model", "model_request"].includes(nodeName)) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const blocks = Array.isArray(token?.contentBlocks)
|
|
459
|
-
? token.contentBlocks
|
|
460
|
-
: Array.isArray(token?.content)
|
|
461
|
-
? token.content
|
|
462
|
-
: [];
|
|
463
|
-
const reasoningDelta = blocks
|
|
464
|
-
.filter((b: any) => b?.type === "reasoning")
|
|
465
|
-
.map((b: any) => String(b.reasoning ?? ""))
|
|
466
|
-
.join("");
|
|
467
|
-
|
|
468
|
-
const textDelta = blocks
|
|
469
|
-
.filter((b: any) => b?.type === "text")
|
|
470
|
-
.map((b: any) => String(b.text ?? ""))
|
|
471
|
-
.join("");
|
|
472
|
-
|
|
473
|
-
if (reasoningDelta) {
|
|
533
|
+
emitReasoningDelta: (reasoningDelta) => {
|
|
474
534
|
const reasoningId = startBlock('reasoning');
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (textDelta) {
|
|
535
|
+
send({
|
|
536
|
+
type: 'reasoning-delta',
|
|
537
|
+
id: reasoningId,
|
|
538
|
+
delta: reasoningDelta,
|
|
539
|
+
});
|
|
540
|
+
},
|
|
541
|
+
emitTextDelta: (textDelta) => {
|
|
483
542
|
const textId = startBlock('text');
|
|
484
543
|
fullResponse += textDelta;
|
|
485
544
|
send({
|
|
@@ -487,16 +546,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
487
546
|
id: textId,
|
|
488
547
|
delta: textDelta,
|
|
489
548
|
});
|
|
490
|
-
}
|
|
491
|
-
}
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
fullResponse = agentResponse.text;
|
|
492
552
|
} catch (error) {
|
|
493
553
|
logger.error(`Agent response streaming failed:\n${formatAgentError(error)}`);
|
|
494
554
|
sequenceDebugCollector.flush();
|
|
555
|
+
fullResponse = formatAgentResponseError(error);
|
|
495
556
|
const textId = startBlock('text');
|
|
496
557
|
send({
|
|
497
558
|
type: 'text-delta',
|
|
498
559
|
id: textId,
|
|
499
|
-
delta:
|
|
560
|
+
delta: fullResponse,
|
|
500
561
|
});
|
|
501
562
|
}
|
|
502
563
|
sequenceDebugCollector.flush();
|
|
@@ -513,6 +574,125 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
513
574
|
return null;
|
|
514
575
|
}
|
|
515
576
|
});
|
|
577
|
+
server.endpoint({
|
|
578
|
+
method: 'POST',
|
|
579
|
+
path: `/agent/speech-response`,
|
|
580
|
+
handler: async ({ body, query, headers, cookies, adminUser, response, requestUrl }) => {
|
|
581
|
+
const audioAdapter = this.options.audioAdapter;
|
|
582
|
+
if (!audioAdapter) {
|
|
583
|
+
response.setStatus(400, undefined);
|
|
584
|
+
return {
|
|
585
|
+
error: "Audio adapter is not configured for AdminForth Agent",
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const speechBody = body as SpeechResponseRequestBody;
|
|
590
|
+
let transcription;
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
transcription = await audioAdapter.transcribe({
|
|
594
|
+
buffer: Buffer.from(speechBody.audioBase64, "base64"),
|
|
595
|
+
filename: speechBody.filename,
|
|
596
|
+
mimeType: speechBody.mimeType,
|
|
597
|
+
language: "auto",
|
|
598
|
+
prompt: speechBody.prompt,
|
|
599
|
+
});
|
|
600
|
+
} catch (error) {
|
|
601
|
+
logger.error(`Agent speech transcription failed:\n${formatAgentError(error)}`);
|
|
602
|
+
response.setStatus(500, undefined);
|
|
603
|
+
return {
|
|
604
|
+
error: "Speech transcription failed. Check server logs for details.",
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const prompt = transcription.text;
|
|
609
|
+
if (!prompt) {
|
|
610
|
+
response.setStatus(400, undefined);
|
|
611
|
+
return {
|
|
612
|
+
error: "Speech transcription is empty",
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const sessionId = speechBody.sessionId || adminUser?.pk || adminUser?.username || 'default';
|
|
617
|
+
const turnId = await this.createNewTurn(sessionId, prompt);
|
|
618
|
+
await this.updateSessionDate(sessionId);
|
|
619
|
+
const sequenceDebugCollector = createSequenceDebugCollector();
|
|
620
|
+
let fullResponse = "";
|
|
621
|
+
|
|
622
|
+
try {
|
|
623
|
+
const agentResponse = await this.runAgentTurn({
|
|
624
|
+
prompt,
|
|
625
|
+
sessionId,
|
|
626
|
+
turnId,
|
|
627
|
+
modeName: speechBody.mode,
|
|
628
|
+
userTimeZone: speechBody.timeZone ?? 'UTC',
|
|
629
|
+
currentPage: speechBody.currentPage,
|
|
630
|
+
adminUser,
|
|
631
|
+
httpExtra: {
|
|
632
|
+
body,
|
|
633
|
+
query,
|
|
634
|
+
headers,
|
|
635
|
+
cookies,
|
|
636
|
+
requestUrl,
|
|
637
|
+
response,
|
|
638
|
+
},
|
|
639
|
+
sequenceDebugCollector,
|
|
640
|
+
emitTextDelta: (textDelta) => {
|
|
641
|
+
fullResponse += textDelta;
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
fullResponse = agentResponse.text;
|
|
645
|
+
const speech = await audioAdapter.synthesize({
|
|
646
|
+
text: fullResponse,
|
|
647
|
+
...speechBody.tts,
|
|
648
|
+
});
|
|
649
|
+
sequenceDebugCollector.flush();
|
|
650
|
+
const turnUpdates: Record<string, unknown> = {
|
|
651
|
+
[this.options.turnResource.responseField]: fullResponse,
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
if (this.options.turnResource.debugField) {
|
|
655
|
+
turnUpdates[this.options.turnResource.debugField] = sequenceDebugCollector.getHistory();
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
await this.updateTurn(turnId, turnUpdates);
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
transcript: {
|
|
662
|
+
text: transcription.text,
|
|
663
|
+
language: transcription.language,
|
|
664
|
+
},
|
|
665
|
+
response: {
|
|
666
|
+
text: fullResponse,
|
|
667
|
+
},
|
|
668
|
+
audio: {
|
|
669
|
+
base64: speech.audio.toString("base64"),
|
|
670
|
+
mimeType: speech.mimeType,
|
|
671
|
+
format: speech.format,
|
|
672
|
+
},
|
|
673
|
+
sessionId,
|
|
674
|
+
turnId,
|
|
675
|
+
};
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger.error(`Agent speech response failed:\n${formatAgentError(error)}`);
|
|
678
|
+
sequenceDebugCollector.flush();
|
|
679
|
+
fullResponse = formatAgentResponseError(error);
|
|
680
|
+
const turnUpdates: Record<string, unknown> = {
|
|
681
|
+
[this.options.turnResource.responseField]: fullResponse,
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
if (this.options.turnResource.debugField) {
|
|
685
|
+
turnUpdates[this.options.turnResource.debugField] = sequenceDebugCollector.getHistory();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
await this.updateTurn(turnId, turnUpdates);
|
|
689
|
+
response.setStatus(500, undefined);
|
|
690
|
+
return {
|
|
691
|
+
error: fullResponse,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
});
|
|
516
696
|
server.endpoint({
|
|
517
697
|
method: 'POST',
|
|
518
698
|
path: `/agent/get-sessions`,
|
|
@@ -630,6 +810,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
630
810
|
ok: true
|
|
631
811
|
};
|
|
632
812
|
}
|
|
813
|
+
}),
|
|
814
|
+
server.endpoint({
|
|
815
|
+
method: 'POST',
|
|
816
|
+
path: `/agent/add-system-message-to-turns`,
|
|
817
|
+
handler: async ({body, adminUser, _raw_express_req }) => {
|
|
818
|
+
const sessionId = body.sessionId;
|
|
819
|
+
const systemMessage = body.systemMessage;
|
|
820
|
+
await this.createNewTurn(sessionId, systemMessage);
|
|
821
|
+
return {
|
|
822
|
+
ok: true
|
|
823
|
+
}
|
|
824
|
+
}
|
|
633
825
|
});
|
|
634
826
|
}
|
|
635
827
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.36.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@langchain/core": "^1.1.40",
|
|
34
34
|
"@langchain/langgraph": "^1.2.8",
|
|
35
35
|
"@langchain/langgraph-checkpoint": "^1.0.1",
|
|
36
|
-
"adminforth": "2.
|
|
36
|
+
"adminforth": "2.51.0",
|
|
37
37
|
"dayjs": "^1.11.20",
|
|
38
38
|
"langchain": "^1.3.3",
|
|
39
39
|
"yaml": "^2.8.3",
|
package/types.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type PluginsCommonOptions,
|
|
3
3
|
type AdminUser,
|
|
4
4
|
type HttpExtra,
|
|
5
|
+
type AudioAdapter,
|
|
5
6
|
} from "adminforth";
|
|
6
7
|
import type { AgentModeCompletionAdapter } from "./agent/simpleAgent.js";
|
|
7
8
|
|
|
@@ -61,6 +62,11 @@ export interface PluginOptions extends PluginsCommonOptions {
|
|
|
61
62
|
completionAdapter: AgentModeCompletionAdapter;
|
|
62
63
|
}[];
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Optional audio adapter for speech-to-text and text-to-speech flows.
|
|
67
|
+
*/
|
|
68
|
+
audioAdapter?: AudioAdapter;
|
|
69
|
+
|
|
64
70
|
/**
|
|
65
71
|
* Max tokens for the generation.
|
|
66
72
|
* Default is 1000
|