@adminforth/agent 1.43.29 → 1.44.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/agentEvents.ts +66 -0
- package/agentResponseEvents.ts +1 -206
- package/build.log +2 -2
- package/custom/conversation_area/ProcessingTimeline.vue +23 -2
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +2 -2
- package/custom/types.ts +5 -4
- package/dist/agent/checkpointer.d.ts +29 -0
- package/dist/agent/languageDetect.d.ts +10 -0
- package/dist/agent/middleware/apiBasedTools.d.ts +3 -0
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +1 -0
- package/dist/agent/middleware/sequenceDebug.d.ts +46 -0
- package/dist/agent/simpleAgent.d.ts +61 -0
- package/dist/agent/skills/registry.d.ts +13 -0
- package/dist/agent/systemPrompt.d.ts +11 -0
- package/dist/agent/toolCallEvents.d.ts +27 -0
- package/dist/agent/tools/apiTool.d.ts +6 -0
- package/dist/agent/tools/fetchSkill.d.ts +8 -0
- package/dist/agent/tools/fetchToolSchema.d.ts +9 -0
- package/dist/agent/tools/getUserLocation.d.ts +8 -0
- package/dist/agent/tools/index.d.ts +4 -0
- package/dist/agentEvents.d.ts +56 -0
- package/dist/agentEvents.js +1 -0
- package/dist/agentResponseEvents.d.ts +1 -0
- package/dist/agentResponseEvents.js +1 -144
- package/dist/apiBasedTools.d.ts +29 -0
- package/dist/custom/conversation_area/ProcessingTimeline.vue +23 -2
- package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +2 -2
- package/dist/custom/types.ts +5 -4
- package/dist/index.d.ts +58 -0
- package/dist/index.js +280 -48
- package/dist/sanitizeSpeechText.d.ts +1 -0
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +14 -0
- package/dist/surfaces/web-sse/createSseEventEmitter.js +211 -0
- package/dist/types.d.ts +94 -0
- package/index.ts +315 -46
- package/package.json +2 -2
- package/surfaces/web-sse/createSseEventEmitter.ts +278 -0
- package/tsconfig.json +1 -0
- package/types.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
|
|
|
24
24
|
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
25
25
|
import { detectUserLanguage } from "./agent/languageDetect.js";
|
|
26
26
|
import { prepareApiBasedTools as buildApiBasedTools } from './apiBasedTools.js';
|
|
27
|
-
import {
|
|
27
|
+
import { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
|
|
28
28
|
import { appendCustomSystemPrompt, buildAgentSystemPrompt, buildAgentTurnSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT } from "./agent/systemPrompt.js";
|
|
29
29
|
import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
|
|
30
30
|
const agentResponseBodySchema = z.object({
|
|
@@ -48,6 +48,8 @@ const sessionIdBodySchema = z.object({
|
|
|
48
48
|
const createSessionBodySchema = z.object({
|
|
49
49
|
triggerMessage: z.string().optional(),
|
|
50
50
|
}).strict();
|
|
51
|
+
const VEGA_LITE_FENCE_START = "```vega-lite";
|
|
52
|
+
const COMPLETE_VEGA_LITE_BLOCK_RE = /```vega-lite[\s\S]*?```/;
|
|
51
53
|
function isAbortError(error) {
|
|
52
54
|
return (error instanceof DOMException && error.name === "AbortError") || (typeof error === "object" &&
|
|
53
55
|
error !== null &&
|
|
@@ -104,6 +106,25 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
104
106
|
}));
|
|
105
107
|
});
|
|
106
108
|
}
|
|
109
|
+
getChatSurfaceSessionId(incoming) {
|
|
110
|
+
return `${incoming.surface}:${incoming.externalConversationId}`;
|
|
111
|
+
}
|
|
112
|
+
getOrCreateChatSurfaceSession(incoming, adminUser) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
const sessionId = this.getChatSurfaceSessionId(incoming);
|
|
115
|
+
const sessionResource = this.adminforth.resource(this.options.sessionResource.resourceId);
|
|
116
|
+
const session = yield sessionResource.get([Filters.EQ(this.options.sessionResource.idField, sessionId)]);
|
|
117
|
+
if (session) {
|
|
118
|
+
return sessionId;
|
|
119
|
+
}
|
|
120
|
+
yield sessionResource.create({
|
|
121
|
+
[this.options.sessionResource.idField]: sessionId,
|
|
122
|
+
[this.options.sessionResource.titleField]: incoming.prompt.slice(0, 40) || "New Session",
|
|
123
|
+
[this.options.sessionResource.askerIdField]: adminUser.pk,
|
|
124
|
+
});
|
|
125
|
+
return sessionId;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
107
128
|
getCheckpointer() {
|
|
108
129
|
if (this.checkpointer)
|
|
109
130
|
return this.checkpointer;
|
|
@@ -170,8 +191,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
170
191
|
});
|
|
171
192
|
}
|
|
172
193
|
validateConfigAfterDiscover(adminforth, resourceConfig) {
|
|
173
|
-
var _a;
|
|
194
|
+
var _a, _b;
|
|
174
195
|
(_a = this.options.audioAdapter) === null || _a === void 0 ? void 0 : _a.validate();
|
|
196
|
+
for (const chatSurfaceAdapter of (_b = this.options.chatSurfaceAdapters) !== null && _b !== void 0 ? _b : []) {
|
|
197
|
+
chatSurfaceAdapter.validate();
|
|
198
|
+
}
|
|
175
199
|
this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth, this.getInternalAgentResourceIds())
|
|
176
200
|
.then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
|
|
177
201
|
}
|
|
@@ -181,8 +205,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
181
205
|
runAgentTurn(input) {
|
|
182
206
|
return __awaiter(this, void 0, void 0, function* () {
|
|
183
207
|
var _a, e_1, _b, _c;
|
|
184
|
-
var _d, _e, _f, _g, _h, _j;
|
|
208
|
+
var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
185
209
|
let fullResponse = "";
|
|
210
|
+
let bufferedTextDelta = "";
|
|
211
|
+
let isRenderingVegaLite = false;
|
|
186
212
|
const maxTokens = (_d = this.options.maxTokens) !== null && _d !== void 0 ? _d : 1000;
|
|
187
213
|
const selectedMode = (_e = this.options.modes.find((mode) => mode.name === input.modeName)) !== null && _e !== void 0 ? _e : this.options.modes[0];
|
|
188
214
|
const [primaryModelSpec, summaryModelSpec] = yield Promise.all([
|
|
@@ -238,14 +264,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
238
264
|
emitToolCallEvent: (event) => {
|
|
239
265
|
var _a;
|
|
240
266
|
input.sequenceDebugCollector.handleToolCallEvent(event);
|
|
241
|
-
(_a = input.
|
|
267
|
+
void ((_a = input.emit) === null || _a === void 0 ? void 0 : _a.call(input, {
|
|
268
|
+
type: "tool-call",
|
|
269
|
+
data: event,
|
|
270
|
+
}));
|
|
242
271
|
},
|
|
243
272
|
sequenceDebugSink: input.sequenceDebugCollector,
|
|
244
273
|
});
|
|
245
274
|
try {
|
|
246
|
-
for (var
|
|
247
|
-
_c =
|
|
248
|
-
|
|
275
|
+
for (var _p = true, _q = __asyncValues(stream), _r; _r = yield _q.next(), _a = _r.done, !_a; _p = true) {
|
|
276
|
+
_c = _r.value;
|
|
277
|
+
_p = false;
|
|
249
278
|
const rawChunk = _c;
|
|
250
279
|
if ((_g = input.abortSignal) === null || _g === void 0 ? void 0 : _g.aborted) {
|
|
251
280
|
throw new DOMException("This operation was aborted", "AbortError");
|
|
@@ -271,21 +300,68 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
271
300
|
.map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
|
|
272
301
|
.join("");
|
|
273
302
|
if (reasoningDelta) {
|
|
274
|
-
(_h = input.
|
|
303
|
+
yield ((_h = input.emit) === null || _h === void 0 ? void 0 : _h.call(input, {
|
|
304
|
+
type: "reasoning-delta",
|
|
305
|
+
delta: reasoningDelta,
|
|
306
|
+
}));
|
|
275
307
|
}
|
|
276
308
|
if (textDelta) {
|
|
277
309
|
fullResponse += textDelta;
|
|
278
|
-
|
|
310
|
+
bufferedTextDelta += textDelta;
|
|
311
|
+
if (bufferedTextDelta.includes(VEGA_LITE_FENCE_START) &&
|
|
312
|
+
!COMPLETE_VEGA_LITE_BLOCK_RE.test(bufferedTextDelta)) {
|
|
313
|
+
if (!isRenderingVegaLite) {
|
|
314
|
+
isRenderingVegaLite = true;
|
|
315
|
+
yield ((_j = input.emit) === null || _j === void 0 ? void 0 : _j.call(input, {
|
|
316
|
+
type: "rendering",
|
|
317
|
+
phase: "start",
|
|
318
|
+
label: "Rendering...",
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (isRenderingVegaLite) {
|
|
324
|
+
isRenderingVegaLite = false;
|
|
325
|
+
yield ((_k = input.emit) === null || _k === void 0 ? void 0 : _k.call(input, {
|
|
326
|
+
type: "rendering",
|
|
327
|
+
phase: "end",
|
|
328
|
+
label: "Rendering...",
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
const streamableLength = bufferedTextDelta.includes(VEGA_LITE_FENCE_START)
|
|
332
|
+
? bufferedTextDelta.length
|
|
333
|
+
: bufferedTextDelta.length - getPartialVegaLiteFenceStartLength(bufferedTextDelta);
|
|
334
|
+
if (!streamableLength) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
yield ((_l = input.emit) === null || _l === void 0 ? void 0 : _l.call(input, {
|
|
338
|
+
type: "text-delta",
|
|
339
|
+
delta: bufferedTextDelta.slice(0, streamableLength),
|
|
340
|
+
}));
|
|
341
|
+
bufferedTextDelta = bufferedTextDelta.slice(streamableLength);
|
|
279
342
|
}
|
|
280
343
|
}
|
|
281
344
|
}
|
|
282
345
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
283
346
|
finally {
|
|
284
347
|
try {
|
|
285
|
-
if (!
|
|
348
|
+
if (!_p && !_a && (_b = _q.return)) yield _b.call(_q);
|
|
286
349
|
}
|
|
287
350
|
finally { if (e_1) throw e_1.error; }
|
|
288
351
|
}
|
|
352
|
+
if (isRenderingVegaLite) {
|
|
353
|
+
yield ((_m = input.emit) === null || _m === void 0 ? void 0 : _m.call(input, {
|
|
354
|
+
type: "rendering",
|
|
355
|
+
phase: "end",
|
|
356
|
+
label: "Rendering...",
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
if (bufferedTextDelta) {
|
|
360
|
+
yield ((_o = input.emit) === null || _o === void 0 ? void 0 : _o.call(input, {
|
|
361
|
+
type: "text-delta",
|
|
362
|
+
delta: bufferedTextDelta,
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
289
365
|
return {
|
|
290
366
|
text: fullResponse,
|
|
291
367
|
};
|
|
@@ -293,7 +369,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
293
369
|
}
|
|
294
370
|
runAndPersistAgentResponse(input) {
|
|
295
371
|
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
-
var _a
|
|
372
|
+
var _a;
|
|
297
373
|
const previousUserMessages = yield this.getPreviousUserMessages(input.sessionId);
|
|
298
374
|
const turnId = yield this.createNewTurn(input.sessionId, input.prompt);
|
|
299
375
|
yield this.adminforth.resource(this.options.sessionResource.resourceId).update(input.sessionId, {
|
|
@@ -315,9 +391,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
315
391
|
abortSignal: input.abortSignal,
|
|
316
392
|
adminUser: input.adminUser,
|
|
317
393
|
sequenceDebugCollector,
|
|
318
|
-
|
|
319
|
-
emitReasoningDelta: input.emitReasoningDelta,
|
|
320
|
-
emitTextDelta: input.emitTextDelta,
|
|
394
|
+
emit: input.emit,
|
|
321
395
|
});
|
|
322
396
|
fullResponse = agentResponse.text;
|
|
323
397
|
}
|
|
@@ -330,7 +404,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
330
404
|
failed = true;
|
|
331
405
|
fullResponse = getErrorMessage(error);
|
|
332
406
|
logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
|
|
333
|
-
(_b = input.emitErrorResponse) === null || _b === void 0 ? void 0 : _b.call(input, fullResponse);
|
|
334
407
|
}
|
|
335
408
|
}
|
|
336
409
|
sequenceDebugCollector.flush();
|
|
@@ -349,7 +422,125 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
349
422
|
};
|
|
350
423
|
});
|
|
351
424
|
}
|
|
425
|
+
handleTurn(input) {
|
|
426
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
427
|
+
var _a, _b;
|
|
428
|
+
yield input.emit({
|
|
429
|
+
type: "turn-started",
|
|
430
|
+
messageId: randomUUID(),
|
|
431
|
+
});
|
|
432
|
+
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
433
|
+
prompt: input.prompt,
|
|
434
|
+
sessionId: input.sessionId,
|
|
435
|
+
modeName: input.modeName,
|
|
436
|
+
userTimeZone: input.userTimeZone,
|
|
437
|
+
currentPage: input.currentPage,
|
|
438
|
+
abortSignal: input.abortSignal,
|
|
439
|
+
adminUser: input.adminUser,
|
|
440
|
+
emit: input.emit,
|
|
441
|
+
failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
|
|
442
|
+
abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
|
|
443
|
+
});
|
|
444
|
+
if (agentResponse.failed) {
|
|
445
|
+
yield input.emit({
|
|
446
|
+
type: "error",
|
|
447
|
+
error: agentResponse.text,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
else if (!agentResponse.aborted) {
|
|
451
|
+
yield input.emit({
|
|
452
|
+
type: "response",
|
|
453
|
+
text: agentResponse.text,
|
|
454
|
+
sessionId: input.sessionId,
|
|
455
|
+
turnId: agentResponse.turnId,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
yield input.emit({
|
|
459
|
+
type: "finish",
|
|
460
|
+
});
|
|
461
|
+
return agentResponse;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
createChatSurfaceEventEmitter(sink) {
|
|
465
|
+
return (event) => __awaiter(this, void 0, void 0, function* () {
|
|
466
|
+
if (event.type === "text-delta") {
|
|
467
|
+
yield sink.emit({
|
|
468
|
+
type: "text_delta",
|
|
469
|
+
delta: event.delta,
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (event.type === "response") {
|
|
474
|
+
yield sink.emit({
|
|
475
|
+
type: "done",
|
|
476
|
+
text: event.text,
|
|
477
|
+
});
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (event.type === "error") {
|
|
481
|
+
yield sink.emit({
|
|
482
|
+
type: "error",
|
|
483
|
+
message: event.error,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
handleChatSurfaceMessage(adapter, incoming, sink) {
|
|
489
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
490
|
+
var _a;
|
|
491
|
+
const adminUser = yield adapter.resolveAdminUser({
|
|
492
|
+
adminforth: this.adminforth,
|
|
493
|
+
incoming,
|
|
494
|
+
});
|
|
495
|
+
if (!adminUser) {
|
|
496
|
+
yield sink.emit({
|
|
497
|
+
type: "error",
|
|
498
|
+
message: "This chat account is not authorized to use AdminForth Agent.",
|
|
499
|
+
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
yield this.handleTurn({
|
|
503
|
+
prompt: incoming.prompt,
|
|
504
|
+
sessionId: yield this.getOrCreateChatSurfaceSession(incoming, adminUser),
|
|
505
|
+
modeName: incoming.modeName,
|
|
506
|
+
userTimeZone: (_a = incoming.userTimeZone) !== null && _a !== void 0 ? _a : "UTC",
|
|
507
|
+
adminUser,
|
|
508
|
+
emit: this.createChatSurfaceEventEmitter(sink),
|
|
509
|
+
failureLogMessage: `Agent ${incoming.surface} surface response failed`,
|
|
510
|
+
abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
}
|
|
352
514
|
setupEndpoints(server) {
|
|
515
|
+
var _a;
|
|
516
|
+
for (const adapter of (_a = this.options.chatSurfaceAdapters) !== null && _a !== void 0 ? _a : []) {
|
|
517
|
+
server.endpoint({
|
|
518
|
+
method: "POST",
|
|
519
|
+
noAuth: true,
|
|
520
|
+
path: `/agent/surface/${adapter.name}/webhook`,
|
|
521
|
+
handler: (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
522
|
+
var _a;
|
|
523
|
+
const surfaceContext = {
|
|
524
|
+
body: ctx.body,
|
|
525
|
+
headers: ctx.headers,
|
|
526
|
+
abortSignal: ctx.abortSignal,
|
|
527
|
+
rawRequest: ctx._raw_express_req,
|
|
528
|
+
rawResponse: ctx._raw_express_res,
|
|
529
|
+
};
|
|
530
|
+
const incoming = yield adapter.parseIncomingMessage(surfaceContext);
|
|
531
|
+
if (!incoming)
|
|
532
|
+
return { ok: true };
|
|
533
|
+
const sink = yield adapter.createEventSink(surfaceContext, incoming);
|
|
534
|
+
try {
|
|
535
|
+
yield this.handleChatSurfaceMessage(adapter, incoming, sink);
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
yield ((_a = sink.close) === null || _a === void 0 ? void 0 : _a.call(sink));
|
|
539
|
+
}
|
|
540
|
+
return { ok: true };
|
|
541
|
+
}),
|
|
542
|
+
});
|
|
543
|
+
}
|
|
353
544
|
server.endpoint({
|
|
354
545
|
method: 'POST',
|
|
355
546
|
path: `/agent/get-placeholder-messages`,
|
|
@@ -378,10 +569,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
378
569
|
const data = this.parseBody(agentResponseBodySchema, body, response);
|
|
379
570
|
if (!data)
|
|
380
571
|
return;
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
572
|
+
const emit = createSseEventEmitter(_raw_express_res, {
|
|
573
|
+
vercelAiUiMessageStream: true,
|
|
574
|
+
closeActiveBlockOnToolStart: true,
|
|
575
|
+
});
|
|
576
|
+
yield this.handleTurn({
|
|
385
577
|
prompt: data.message,
|
|
386
578
|
sessionId: data.sessionId,
|
|
387
579
|
modeName: data.mode,
|
|
@@ -389,14 +581,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
389
581
|
currentPage: data.currentPage,
|
|
390
582
|
abortSignal,
|
|
391
583
|
adminUser: currentAdminUser,
|
|
392
|
-
|
|
393
|
-
emitReasoningDelta: stream.reasoningDelta,
|
|
394
|
-
emitTextDelta: stream.textDelta,
|
|
395
|
-
emitErrorResponse: stream.textDelta,
|
|
584
|
+
emit,
|
|
396
585
|
failureLogMessage: "Agent response streaming failed",
|
|
397
586
|
abortLogMessage: "Agent response streaming aborted by the client",
|
|
398
587
|
});
|
|
399
|
-
stream.end();
|
|
400
588
|
return null;
|
|
401
589
|
})
|
|
402
590
|
});
|
|
@@ -420,7 +608,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
420
608
|
response.setStatus(400, "Audio file is required");
|
|
421
609
|
return { error: "Audio file is required" };
|
|
422
610
|
}
|
|
423
|
-
const
|
|
611
|
+
const emit = createSseEventEmitter(_raw_express_res);
|
|
424
612
|
let transcription;
|
|
425
613
|
try {
|
|
426
614
|
transcription = yield audioAdapter.transcribe({
|
|
@@ -434,25 +622,35 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
434
622
|
catch (error) {
|
|
435
623
|
if (abortSignal.aborted || isAbortError(error)) {
|
|
436
624
|
logger.info("Agent speech transcription aborted by the client");
|
|
437
|
-
|
|
625
|
+
yield emit({ type: "finish" });
|
|
438
626
|
return null;
|
|
439
627
|
}
|
|
440
628
|
logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
|
|
441
|
-
|
|
442
|
-
|
|
629
|
+
yield emit({
|
|
630
|
+
type: "error",
|
|
631
|
+
error: "Speech transcription failed. Check server logs for details.",
|
|
632
|
+
});
|
|
633
|
+
yield emit({ type: "finish" });
|
|
443
634
|
return null;
|
|
444
635
|
}
|
|
445
636
|
if (abortSignal.aborted) {
|
|
446
|
-
|
|
637
|
+
yield emit({ type: "finish" });
|
|
447
638
|
return null;
|
|
448
639
|
}
|
|
449
640
|
const prompt = transcription.text;
|
|
450
641
|
if (!prompt) {
|
|
451
|
-
|
|
452
|
-
|
|
642
|
+
yield emit({
|
|
643
|
+
type: "error",
|
|
644
|
+
error: "Speech transcription is empty",
|
|
645
|
+
});
|
|
646
|
+
yield emit({ type: "finish" });
|
|
453
647
|
return null;
|
|
454
648
|
}
|
|
455
|
-
|
|
649
|
+
yield emit({
|
|
650
|
+
type: "transcript",
|
|
651
|
+
text: transcription.text,
|
|
652
|
+
language: transcription.language,
|
|
653
|
+
});
|
|
456
654
|
const sessionId = data.sessionId;
|
|
457
655
|
const currentPage = data.currentPage;
|
|
458
656
|
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
@@ -463,26 +661,39 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
463
661
|
currentPage,
|
|
464
662
|
abortSignal,
|
|
465
663
|
adminUser: currentAdminUser,
|
|
466
|
-
|
|
664
|
+
emit: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
665
|
+
if (event.type === "tool-call") {
|
|
666
|
+
yield emit(event);
|
|
667
|
+
}
|
|
668
|
+
}),
|
|
467
669
|
failureLogMessage: "Agent speech response failed",
|
|
468
670
|
abortLogMessage: "Agent speech response aborted by the client",
|
|
469
671
|
});
|
|
470
672
|
if (agentResponse.aborted) {
|
|
471
|
-
|
|
673
|
+
yield emit({ type: "finish" });
|
|
472
674
|
return null;
|
|
473
675
|
}
|
|
474
676
|
if (agentResponse.failed) {
|
|
475
|
-
|
|
476
|
-
|
|
677
|
+
yield emit({
|
|
678
|
+
type: "error",
|
|
679
|
+
error: agentResponse.text,
|
|
680
|
+
});
|
|
681
|
+
yield emit({ type: "finish" });
|
|
477
682
|
return null;
|
|
478
683
|
}
|
|
479
684
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
685
|
+
yield emit({
|
|
686
|
+
type: "speech-response",
|
|
687
|
+
transcript: {
|
|
688
|
+
text: transcription.text,
|
|
689
|
+
language: transcription.language,
|
|
690
|
+
},
|
|
691
|
+
response: {
|
|
692
|
+
text: agentResponse.text,
|
|
693
|
+
},
|
|
694
|
+
sessionId,
|
|
695
|
+
turnId: agentResponse.turnId,
|
|
696
|
+
});
|
|
486
697
|
const speech = yield audioAdapter.synthesize({
|
|
487
698
|
text: sanitizeSpeechText(agentResponse.text),
|
|
488
699
|
stream: true,
|
|
@@ -490,7 +701,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
490
701
|
format: "pcm",
|
|
491
702
|
abortSignal,
|
|
492
703
|
});
|
|
493
|
-
|
|
704
|
+
yield emit({
|
|
705
|
+
type: "audio-start",
|
|
706
|
+
mimeType: speech.mimeType,
|
|
707
|
+
format: speech.format,
|
|
708
|
+
sampleRate: 24000,
|
|
709
|
+
channelCount: 1,
|
|
710
|
+
bitsPerSample: 16,
|
|
711
|
+
});
|
|
494
712
|
const reader = speech.audioStream.getReader();
|
|
495
713
|
const cancelAudioStream = () => {
|
|
496
714
|
void reader.cancel().catch(() => undefined);
|
|
@@ -509,15 +727,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
509
727
|
if (abortSignal.aborted) {
|
|
510
728
|
break;
|
|
511
729
|
}
|
|
512
|
-
|
|
730
|
+
yield emit({
|
|
731
|
+
type: "audio-delta",
|
|
732
|
+
value,
|
|
733
|
+
});
|
|
513
734
|
}
|
|
514
735
|
}
|
|
515
736
|
finally {
|
|
516
737
|
abortSignal.removeEventListener("abort", cancelAudioStream);
|
|
517
738
|
reader.releaseLock();
|
|
518
739
|
}
|
|
519
|
-
|
|
520
|
-
|
|
740
|
+
yield emit({ type: "audio-done" });
|
|
741
|
+
yield emit({ type: "finish" });
|
|
521
742
|
return null;
|
|
522
743
|
}
|
|
523
744
|
catch (error) {
|
|
@@ -526,9 +747,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
526
747
|
}
|
|
527
748
|
else {
|
|
528
749
|
logger.error(`Agent speech audio streaming failed:\n${error}`);
|
|
529
|
-
|
|
750
|
+
yield emit({
|
|
751
|
+
type: "error",
|
|
752
|
+
error: getErrorMessage(error),
|
|
753
|
+
});
|
|
530
754
|
}
|
|
531
|
-
|
|
755
|
+
yield emit({ type: "finish" });
|
|
532
756
|
return null;
|
|
533
757
|
}
|
|
534
758
|
})
|
|
@@ -674,3 +898,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
674
898
|
});
|
|
675
899
|
}
|
|
676
900
|
}
|
|
901
|
+
function getPartialVegaLiteFenceStartLength(text) {
|
|
902
|
+
for (let length = Math.min(text.length, VEGA_LITE_FENCE_START.length - 1); length > 0; length -= 1) {
|
|
903
|
+
if (VEGA_LITE_FENCE_START.startsWith(text.slice(-length))) {
|
|
904
|
+
return length;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return 0;
|
|
908
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sanitizeSpeechText(input: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentEventEmitter } from "../../agentEvents.js";
|
|
2
|
+
type AgentEventStreamResponse = {
|
|
3
|
+
writeHead: (statusCode: number, headers: Record<string, string>) => void;
|
|
4
|
+
write: (chunk: string) => unknown;
|
|
5
|
+
end: () => unknown;
|
|
6
|
+
writableEnded: boolean;
|
|
7
|
+
destroyed: boolean;
|
|
8
|
+
};
|
|
9
|
+
type AgentEventStreamOptions = {
|
|
10
|
+
vercelAiUiMessageStream?: boolean;
|
|
11
|
+
closeActiveBlockOnToolStart?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function createSseEventEmitter(res: AgentEventStreamResponse, options?: AgentEventStreamOptions): AgentEventEmitter;
|
|
14
|
+
export {};
|