@adminforth/agent 1.50.1 → 1.51.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.
Files changed (54) hide show
  1. package/agent/middleware/apiBasedTools.ts +13 -3
  2. package/agent/models/AgentModeResolver.ts +9 -0
  3. package/agent/models/AgentModelFactory.ts +28 -0
  4. package/agent/runtime/AgentContext.ts +30 -0
  5. package/agent/runtime/AgentRuntime.ts +68 -0
  6. package/agent/simpleAgent.ts +2 -129
  7. package/agent/speech/SpeechTurnService.ts +179 -0
  8. package/agent/tools/AgentToolProvider.ts +28 -0
  9. package/agent/tools/navigateUser.ts +2 -2
  10. package/agent/turn/TurnContextBuilder.ts +36 -0
  11. package/agent/turn/TurnLifecycleService.ts +29 -0
  12. package/agent/turn/TurnPersistenceService.ts +33 -0
  13. package/agent/turn/TurnPromptBuilder.ts +51 -0
  14. package/agent/turn/TurnStreamConsumer.ts +61 -0
  15. package/agent/turn/VegaLiteStreamBuffer.ts +90 -0
  16. package/agent/turn/turnTypes.ts +92 -0
  17. package/agentTurnService.ts +88 -461
  18. package/build.log +1 -1
  19. package/dist/agent/middleware/apiBasedTools.js +9 -2
  20. package/dist/agent/models/AgentModeResolver.d.ts +9 -0
  21. package/dist/agent/models/AgentModeResolver.js +9 -0
  22. package/dist/agent/models/AgentModelFactory.d.ts +7 -0
  23. package/dist/agent/models/AgentModelFactory.js +36 -0
  24. package/dist/agent/runtime/AgentContext.d.ts +28 -0
  25. package/dist/agent/runtime/AgentContext.js +17 -0
  26. package/dist/agent/runtime/AgentRuntime.d.ts +15 -0
  27. package/dist/agent/runtime/AgentRuntime.js +57 -0
  28. package/dist/agent/simpleAgent.d.ts +15 -45
  29. package/dist/agent/simpleAgent.js +1 -67
  30. package/dist/agent/speech/SpeechTurnService.d.ts +6 -0
  31. package/dist/agent/speech/SpeechTurnService.js +168 -0
  32. package/dist/agent/tools/AgentToolProvider.d.ts +9 -0
  33. package/dist/agent/tools/AgentToolProvider.js +27 -0
  34. package/dist/agent/tools/navigateUser.js +1 -1
  35. package/dist/agent/turn/TurnContextBuilder.d.ts +14 -0
  36. package/dist/agent/turn/TurnContextBuilder.js +31 -0
  37. package/dist/agent/turn/TurnLifecycleService.d.ts +17 -0
  38. package/dist/agent/turn/TurnLifecycleService.js +31 -0
  39. package/dist/agent/turn/TurnPersistenceService.d.ts +13 -0
  40. package/dist/agent/turn/TurnPersistenceService.js +35 -0
  41. package/dist/agent/turn/TurnPromptBuilder.d.ts +19 -0
  42. package/dist/agent/turn/TurnPromptBuilder.js +43 -0
  43. package/dist/agent/turn/TurnStreamConsumer.d.ts +10 -0
  44. package/dist/agent/turn/TurnStreamConsumer.js +78 -0
  45. package/dist/agent/turn/VegaLiteStreamBuffer.d.ts +7 -0
  46. package/dist/agent/turn/VegaLiteStreamBuffer.js +87 -0
  47. package/dist/agent/turn/turnTypes.d.ts +81 -0
  48. package/dist/agent/turn/turnTypes.js +1 -0
  49. package/dist/agentTurnService.d.ts +20 -69
  50. package/dist/agentTurnService.js +60 -373
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +22 -7
  53. package/index.ts +35 -7
  54. package/package.json +6 -3
@@ -1,298 +1,102 @@
1
- import type { AdminUser, AudioAdapter, IAdminForth } from "adminforth";
2
1
  import { logger } from "adminforth";
3
2
  import { randomUUID } from "crypto";
4
- import { HumanMessage, SystemMessage } from "langchain";
5
- import type { BaseCheckpointSaver } from "@langchain/langgraph";
6
- import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
3
+ import { AgentModelFactory } from "./agent/models/AgentModelFactory.js";
4
+ import { AgentModeResolver } from "./agent/models/AgentModeResolver.js";
7
5
  import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
8
- import { detectUserLanguage, type PreviousUserMessage } from "./agent/languageDetect.js";
9
- import { prepareApiBasedTools as buildApiBasedTools } from "./apiBasedTools.js";
10
- import type { AgentEventEmitter } from "./agentEvents.js";
11
- import { buildAgentTurnSystemPrompt } from "./agent/systemPrompt.js";
12
- import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
13
- import { isAbortError, getErrorMessage } from "./errors.js";
14
- import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
15
- import type { AgentSessionStore } from "./sessionStore.js";
16
- import type { PluginOptions } from "./types.js";
17
-
18
- type AgentTurnRunInput = {
19
- prompt: string;
20
- sessionId: string;
21
- turnId: string;
22
- previousUserMessages: PreviousUserMessage[];
23
- modeName?: string | null;
24
- userTimeZone: string;
25
- currentPage?: CurrentPageContext;
26
- chatSurface?: string;
27
- adminPublicOrigin?: string;
28
- abortSignal?: AbortSignal;
29
- adminUser: AdminUser;
30
- sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
31
- emit?: AgentEventEmitter;
32
- };
33
-
34
- export type RunAndPersistAgentResponseInput = {
35
- prompt: string;
36
- sessionId: string;
37
- modeName?: string | null;
38
- userTimeZone: string;
39
- currentPage?: CurrentPageContext;
40
- chatSurface?: string;
41
- adminPublicOrigin?: string;
42
- abortSignal?: AbortSignal;
43
- adminUser: AdminUser;
44
- emit?: AgentEventEmitter;
45
- failureLogMessage: string;
46
- abortLogMessage: string;
47
- };
48
-
49
- export type RunAndPersistAgentResponseResult = {
50
- text: string;
51
- turnId: string;
52
- aborted: boolean;
53
- failed: boolean;
54
- };
55
-
56
- export type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
57
- emit: AgentEventEmitter;
58
- failureLogMessage?: string;
59
- abortLogMessage?: string;
60
- };
61
-
62
- export type HandleSpeechTurnInput = Omit<HandleTurnInput, "prompt"> & {
63
- audioAdapter: AudioAdapter;
64
- audio: {
65
- buffer: Buffer;
66
- filename: string;
67
- mimeType: string;
68
- };
69
- };
70
-
71
- type AgentTurnServiceOptions = {
72
- getAdminforth: () => IAdminForth;
73
- getPluginInstanceId: () => string;
74
- options: PluginOptions;
75
- sessionStore: AgentSessionStore;
76
- getCheckpointer: () => BaseCheckpointSaver;
77
- getInternalAgentResourceIds: () => string[];
78
- getAgentSystemPrompt: () => Promise<string>;
79
- };
80
-
81
- const VEGA_LITE_FENCE_START = "```vega-lite";
82
- const COMPLETE_VEGA_LITE_BLOCK_RE = /```vega-lite[\s\S]*?```/;
6
+ import { AgentRuntime } from "./agent/runtime/AgentRuntime.js";
7
+ import { TurnContextBuilder } from "./agent/turn/TurnContextBuilder.js";
8
+ import { TurnLifecycleService } from "./agent/turn/TurnLifecycleService.js";
9
+ import { TurnPromptBuilder } from "./agent/turn/TurnPromptBuilder.js";
10
+ import { TurnStreamConsumer } from "./agent/turn/TurnStreamConsumer.js";
11
+ import type {
12
+ BaseAgentTurnInput,
13
+ HandleTurnInput,
14
+ PreparedAgentTurn,
15
+ RunAndPersistAgentResponseInput,
16
+ RunAndPersistAgentResponseResult,
17
+ } from "./agent/turn/turnTypes.js";
18
+ import { getErrorMessage, isAbortError } from "./errors.js";
19
+
20
+ export type {
21
+ BaseAgentTurnInput,
22
+ HandleSpeechTurnInput,
23
+ HandleTurnInput,
24
+ RunAndPersistAgentResponseInput,
25
+ RunAndPersistAgentResponseResult,
26
+ } from "./agent/turn/turnTypes.js";
83
27
 
84
28
  export class AgentTurnService {
85
- constructor(private serviceOptions: AgentTurnServiceOptions) {}
86
-
87
- private async runAgentTurn(input: AgentTurnRunInput) {
88
- const adminforth = this.serviceOptions.getAdminforth();
89
- const options = this.serviceOptions.options;
90
- let fullResponse = "";
91
- let bufferedTextDelta = "";
92
- let isRenderingVegaLite = false;
93
- const maxTokens = options.maxTokens ?? 1000;
94
- const selectedMode = options.modes.find((mode) => mode.name === input.modeName) ?? options.modes[0];
95
- const [primaryModelSpec, summaryModelSpec] = await Promise.all([
96
- createAgentChatModel({
97
- adapter: selectedMode.completionAdapter,
98
- maxTokens,
99
- purpose: "primary",
100
- }),
101
- createAgentChatModel({
102
- adapter: selectedMode.completionAdapter,
103
- maxTokens,
104
- purpose: "summary",
105
- }),
106
- ]);
107
- const model = primaryModelSpec.model;
108
- const summaryModel = summaryModelSpec.model;
109
- const modelMiddleware = primaryModelSpec.middleware;
110
-
111
- const userLanguage = await detectUserLanguage(selectedMode.completionAdapter, input.prompt, input.previousUserMessages)
112
- .catch((error) => {
113
- if (input.abortSignal?.aborted || isAbortError(error)) {
114
- throw error;
115
- }
116
-
117
- logger.warn(`Failed to detect user language: ${getErrorMessage(error)}`);
118
- return null;
119
- });
120
- const systemPrompt = buildAgentTurnSystemPrompt({
121
- agentSystemPrompt: await this.serviceOptions.getAgentSystemPrompt(),
122
- adminUser: input.adminUser,
123
- usernameField: adminforth.config.auth!.usernameField,
124
- userLanguage,
125
- chatSurface: input.chatSurface,
29
+ constructor(
30
+ private readonly lifecycle: TurnLifecycleService,
31
+ private readonly contextBuilder: TurnContextBuilder,
32
+ private readonly modeResolver: AgentModeResolver,
33
+ private readonly modelFactory: AgentModelFactory,
34
+ private readonly promptBuilder: TurnPromptBuilder,
35
+ private readonly runtime: AgentRuntime,
36
+ private readonly streamConsumer: TurnStreamConsumer,
37
+ ) {}
38
+
39
+ private async prepareTurn(input: BaseAgentTurnInput): Promise<PreparedAgentTurn> {
40
+ const sequenceDebugCollector = createSequenceDebugCollector();
41
+ const { turnId, previousUserMessages } = await this.lifecycle.start(input);
42
+ const context = await this.contextBuilder.build({
43
+ base: input,
44
+ turnId,
126
45
  });
127
- const apiBasedTools = buildApiBasedTools(
128
- adminforth,
129
- this.serviceOptions.getInternalAgentResourceIds(),
130
- );
131
46
 
132
- const stream = await callAgent({
133
- name: `adminforth-agent-${this.serviceOptions.getPluginInstanceId()}`,
134
- model,
135
- summaryModel,
136
- modelMiddleware,
137
- checkpointer: this.serviceOptions.getCheckpointer(),
138
- messages: [
139
- new SystemMessage(systemPrompt),
140
- new HumanMessage(input.prompt),
141
- ],
142
- adminUser: input.adminUser,
143
- adminforth,
144
- apiBasedTools,
145
- customComponentsDir: adminforth.config.customization.customComponentsDir ?? "custom",
146
- pluginCustomFolderPaths: adminforth.activatedPlugins.map((plugin) => plugin.customFolderPath),
47
+ return {
48
+ prompt: input.prompt,
147
49
  sessionId: input.sessionId,
148
- turnId: input.turnId,
149
- currentPage: input.currentPage,
150
- chatSurface: input.chatSurface,
151
- adminPublicOrigin: input.adminPublicOrigin,
152
- userTimeZone: input.userTimeZone,
153
- abortSignal: input.abortSignal,
154
- emitToolCallEvent: (event) => {
155
- input.sequenceDebugCollector.handleToolCallEvent(event);
156
- void input.emit?.({
157
- type: "tool-call",
158
- data: event,
159
- });
50
+ turnId,
51
+ previousUserMessages,
52
+ modeName: input.modeName,
53
+ context,
54
+ observability: {
55
+ emit: undefined,
56
+ sequenceDebugSink: sequenceDebugCollector,
160
57
  },
161
- emitAgentEvent: input.emit,
162
- sequenceDebugSink: input.sequenceDebugCollector,
163
- });
164
-
165
- for await (const rawChunk of stream as AsyncIterable<[any, any]>) {
166
- if (input.abortSignal?.aborted) {
167
- throw new DOMException("This operation was aborted", "AbortError");
168
- }
169
-
170
- const [token, metadata] = rawChunk;
171
-
172
- const nodeName =
173
- typeof metadata?.langgraph_node === "string"
174
- ? metadata.langgraph_node
175
- : "";
176
-
177
- if (nodeName && !["model", "model_request"].includes(nodeName)) {
178
- continue;
179
- }
180
-
181
- const blocks = Array.isArray(token?.contentBlocks)
182
- ? token.contentBlocks
183
- : Array.isArray(token?.content)
184
- ? token.content
185
- : [];
186
- const reasoningDelta = blocks
187
- .filter((b: any) => b?.type === "reasoning")
188
- .map((b: any) => String(b.reasoning ?? ""))
189
- .join("");
190
-
191
- const textDelta = blocks
192
- .filter((b: any) => b?.type === "text")
193
- .map((b: any) => String(b.text ?? ""))
194
- .join("");
195
-
196
- if (reasoningDelta) {
197
- await input.emit?.({
198
- type: "reasoning-delta",
199
- delta: reasoningDelta,
200
- });
201
- }
202
-
203
- if (textDelta) {
204
- fullResponse += textDelta;
205
- bufferedTextDelta += textDelta;
206
-
207
- if (
208
- bufferedTextDelta.includes(VEGA_LITE_FENCE_START) &&
209
- !COMPLETE_VEGA_LITE_BLOCK_RE.test(bufferedTextDelta)
210
- ) {
211
- if (!isRenderingVegaLite) {
212
- isRenderingVegaLite = true;
213
- await input.emit?.({
214
- type: "rendering",
215
- phase: "start",
216
- label: "Rendering...",
217
- });
218
- }
219
- continue;
220
- }
221
-
222
- if (isRenderingVegaLite) {
223
- isRenderingVegaLite = false;
224
- await input.emit?.({
225
- type: "rendering",
226
- phase: "end",
227
- label: "Rendering...",
228
- });
229
- }
230
-
231
- const streamableLength = bufferedTextDelta.includes(VEGA_LITE_FENCE_START)
232
- ? bufferedTextDelta.length
233
- : bufferedTextDelta.length - getPartialVegaLiteFenceStartLength(bufferedTextDelta);
234
-
235
- if (!streamableLength) {
236
- continue;
237
- }
238
-
239
- await input.emit?.({
240
- type: "text-delta",
241
- delta: bufferedTextDelta.slice(0, streamableLength),
242
- });
243
- bufferedTextDelta = bufferedTextDelta.slice(streamableLength);
244
- }
245
- }
246
-
247
- if (isRenderingVegaLite) {
248
- await input.emit?.({
249
- type: "rendering",
250
- phase: "end",
251
- label: "Rendering...",
252
- });
253
- }
254
-
255
- if (bufferedTextDelta) {
256
- await input.emit?.({
257
- type: "text-delta",
258
- delta: bufferedTextDelta,
259
- });
260
- }
261
-
262
- return {
263
- text: fullResponse,
264
58
  };
265
59
  }
266
60
 
267
- async runAndPersistAgentResponse(input: RunAndPersistAgentResponseInput) {
268
- const adminforth = this.serviceOptions.getAdminforth();
269
- const options = this.serviceOptions.options;
270
- const previousUserMessages = await this.serviceOptions.sessionStore.getPreviousUserMessages(input.sessionId);
271
- const turnId = await this.serviceOptions.sessionStore.createNewTurn(input.sessionId, input.prompt);
272
- await adminforth.resource(options.sessionResource.resourceId).update(input.sessionId, {
273
- [options.sessionResource.createdAtField]: new Date().toISOString(),
61
+ private async runAgentTurn(input: PreparedAgentTurn) {
62
+ const selectedMode = this.modeResolver.resolve(input.modeName);
63
+ const [models, messages] = await Promise.all([
64
+ this.modelFactory.create(selectedMode.completionAdapter),
65
+ this.promptBuilder.build({
66
+ prompt: input.prompt,
67
+ previousUserMessages: input.previousUserMessages,
68
+ adminUser: input.context.adminUser,
69
+ completionAdapter: selectedMode.completionAdapter,
70
+ chatSurface: input.context.chatSurface,
71
+ abortSignal: input.context.abortSignal,
72
+ }),
73
+ ]);
74
+ const stream = await this.runtime.stream({
75
+ models,
76
+ messages,
77
+ context: input.context,
78
+ observability: input.observability,
274
79
  });
275
- const sequenceDebugCollector = createSequenceDebugCollector();
80
+
81
+ return this.streamConsumer.consume({
82
+ stream: stream as AsyncIterable<[any, any]>,
83
+ abortSignal: input.context.abortSignal,
84
+ emit: input.observability.emit,
85
+ });
86
+ }
87
+
88
+ async runAndPersistAgentResponse(
89
+ input: RunAndPersistAgentResponseInput,
90
+ ): Promise<RunAndPersistAgentResponseResult> {
91
+ const preparedTurn = await this.prepareTurn(input);
92
+ preparedTurn.observability.emit = input.emit;
93
+
276
94
  let fullResponse = "";
277
95
  let aborted = false;
278
96
  let failed = false;
279
97
 
280
98
  try {
281
- const agentResponse = await this.runAgentTurn({
282
- prompt: input.prompt,
283
- sessionId: input.sessionId,
284
- turnId,
285
- previousUserMessages,
286
- modeName: input.modeName,
287
- userTimeZone: input.userTimeZone,
288
- currentPage: input.currentPage,
289
- chatSurface: input.chatSurface,
290
- adminPublicOrigin: input.adminPublicOrigin,
291
- abortSignal: input.abortSignal,
292
- adminUser: input.adminUser,
293
- sequenceDebugCollector,
294
- emit: input.emit,
295
- });
99
+ const agentResponse = await this.runAgentTurn(preparedTurn);
296
100
  fullResponse = agentResponse.text;
297
101
  } catch (error) {
298
102
  if (input.abortSignal?.aborted || isAbortError(error)) {
@@ -305,20 +109,16 @@ export class AgentTurnService {
305
109
  }
306
110
  }
307
111
 
308
- sequenceDebugCollector.flush();
309
- const turnUpdates: Record<string, unknown> = {
310
- [options.turnResource.responseField]: fullResponse,
311
- };
312
-
313
- if (options.turnResource.debugField) {
314
- turnUpdates[options.turnResource.debugField] = sequenceDebugCollector.getHistory();
315
- }
316
-
317
- await adminforth.resource(options.turnResource.resourceId).update(turnId, turnUpdates);
112
+ preparedTurn.observability.sequenceDebugSink.flush();
113
+ await this.lifecycle.finish({
114
+ turnId: preparedTurn.turnId,
115
+ responseText: fullResponse,
116
+ debugHistory: preparedTurn.observability.sequenceDebugSink.getHistory(),
117
+ });
318
118
 
319
119
  return {
320
120
  text: fullResponse,
321
- turnId,
121
+ turnId: preparedTurn.turnId,
322
122
  aborted,
323
123
  failed,
324
124
  };
@@ -365,177 +165,4 @@ export class AgentTurnService {
365
165
 
366
166
  return agentResponse;
367
167
  }
368
-
369
- async handleSpeechTurn(input: HandleSpeechTurnInput) {
370
- let transcription;
371
-
372
- try {
373
- transcription = await input.audioAdapter.transcribe({
374
- buffer: input.audio.buffer,
375
- filename: input.audio.filename,
376
- mimeType: input.audio.mimeType,
377
- language: "auto",
378
- abortSignal: input.abortSignal,
379
- });
380
- } catch (error) {
381
- if (input.abortSignal?.aborted || isAbortError(error)) {
382
- logger.info("Agent speech transcription aborted by the client");
383
- await input.emit({ type: "finish" });
384
- return null;
385
- }
386
-
387
- logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
388
- await input.emit({
389
- type: "error",
390
- error: "Speech transcription failed. Check server logs for details.",
391
- });
392
- await input.emit({ type: "finish" });
393
- return null;
394
- }
395
-
396
- if (input.abortSignal?.aborted) {
397
- await input.emit({ type: "finish" });
398
- return null;
399
- }
400
-
401
- const prompt = transcription.text;
402
- if (!prompt) {
403
- await input.emit({
404
- type: "error",
405
- error: "Speech transcription is empty",
406
- });
407
- await input.emit({ type: "finish" });
408
- return null;
409
- }
410
-
411
- await input.emit({
412
- type: "transcript",
413
- text: transcription.text,
414
- language: transcription.language,
415
- });
416
-
417
- const agentResponse = await this.runAndPersistAgentResponse({
418
- prompt,
419
- sessionId: input.sessionId,
420
- modeName: input.modeName,
421
- userTimeZone: input.userTimeZone,
422
- currentPage: input.currentPage,
423
- chatSurface: input.chatSurface,
424
- adminPublicOrigin: input.adminPublicOrigin,
425
- abortSignal: input.abortSignal,
426
- adminUser: input.adminUser,
427
- emit: async (event) => {
428
- if (event.type === "tool-call") {
429
- await input.emit(event);
430
- }
431
- },
432
- failureLogMessage: input.failureLogMessage ?? "Agent speech response failed",
433
- abortLogMessage: input.abortLogMessage ?? "Agent speech response aborted by the client",
434
- });
435
-
436
- if (agentResponse.aborted) {
437
- await input.emit({ type: "finish" });
438
- return agentResponse;
439
- }
440
-
441
- if (agentResponse.failed) {
442
- await input.emit({
443
- type: "error",
444
- error: agentResponse.text,
445
- });
446
- await input.emit({ type: "finish" });
447
- return agentResponse;
448
- }
449
-
450
- try {
451
- await input.emit({
452
- type: "speech-response",
453
- transcript: {
454
- text: transcription.text,
455
- language: transcription.language,
456
- },
457
- response: {
458
- text: agentResponse.text,
459
- },
460
- sessionId: input.sessionId,
461
- turnId: agentResponse.turnId,
462
- });
463
- const speech = await input.audioAdapter.synthesize({
464
- text: sanitizeSpeechText(agentResponse.text),
465
- stream: true,
466
- streamFormat: "audio",
467
- format: "pcm",
468
- abortSignal: input.abortSignal,
469
- });
470
-
471
- await input.emit({
472
- type: "audio-start",
473
- mimeType: speech.mimeType,
474
- format: speech.format,
475
- sampleRate: 24000,
476
- channelCount: 1,
477
- bitsPerSample: 16,
478
- });
479
-
480
- const reader = speech.audioStream.getReader();
481
- const cancelAudioStream = () => {
482
- void reader.cancel().catch(() => undefined);
483
- };
484
-
485
- try {
486
- input.abortSignal?.addEventListener("abort", cancelAudioStream, { once: true });
487
-
488
- while (true) {
489
- if (input.abortSignal?.aborted) {
490
- await reader.cancel().catch(() => undefined);
491
- break;
492
- }
493
-
494
- const { value, done } = await reader.read();
495
-
496
- if (done) {
497
- break;
498
- }
499
-
500
- if (input.abortSignal?.aborted) {
501
- break;
502
- }
503
-
504
- await input.emit({
505
- type: "audio-delta",
506
- value,
507
- });
508
- }
509
- } finally {
510
- input.abortSignal?.removeEventListener("abort", cancelAudioStream);
511
- reader.releaseLock();
512
- }
513
-
514
- await input.emit({ type: "audio-done" });
515
- await input.emit({ type: "finish" });
516
- return agentResponse;
517
- } catch (error) {
518
- if (input.abortSignal?.aborted || isAbortError(error)) {
519
- logger.info("Agent speech audio streaming aborted by the client");
520
- } else {
521
- logger.error(`Agent speech audio streaming failed:\n${getErrorMessage(error)}`);
522
- await input.emit({
523
- type: "error",
524
- error: getErrorMessage(error),
525
- });
526
- }
527
- await input.emit({ type: "finish" });
528
- return agentResponse;
529
- }
530
- }
531
- }
532
-
533
- function getPartialVegaLiteFenceStartLength(text: string): number {
534
- for (let length = Math.min(text.length, VEGA_LITE_FENCE_START.length - 1); length > 0; length -= 1) {
535
- if (VEGA_LITE_FENCE_START.startsWith(text.slice(-length))) {
536
- return length;
537
- }
538
- }
539
-
540
- return 0;
541
168
  }
package/build.log CHANGED
@@ -62,5 +62,5 @@ custom/speech_recognition_frontend/voiceActivityDetection.ts
62
62
  custom/speech_recognition_frontend/types/
63
63
  custom/speech_recognition_frontend/types/voice-activity-detection.d.ts
64
64
 
65
- sent 1,670,943 bytes received 921 bytes 3,343,728.00 bytes/sec
65
+ sent 1,670,885 bytes received 921 bytes 3,343,612.00 bytes/sec
66
66
  total size is 1,666,790 speedup is 1.00
@@ -65,7 +65,14 @@ export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
65
65
  var _a, _b, _c, _d;
66
66
  const startedAt = Date.now();
67
67
  const toolInput = JSON.stringify((_a = request.toolCall.args) !== null && _a !== void 0 ? _a : {});
68
- const { adminUser, emitToolCallEvent, userTimeZone } = request.runtime.context;
68
+ const { adminUser, emit, sequenceDebugSink, userTimeZone } = request.runtime.context;
69
+ const emitToolCall = (event) => {
70
+ sequenceDebugSink.handleToolCallEvent(event);
71
+ void (emit === null || emit === void 0 ? void 0 : emit({
72
+ type: "tool-call",
73
+ data: event,
74
+ }));
75
+ };
69
76
  const toolArgs = ((_b = request.toolCall.args) !== null && _b !== void 0 ? _b : {});
70
77
  let toolInfo;
71
78
  if (request.toolCall.name === "fetch_skill") {
@@ -84,7 +91,7 @@ export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
84
91
  });
85
92
  }
86
93
  const toolCallTracker = createToolCallTracker({
87
- emit: emitToolCallEvent,
94
+ emit: emitToolCall,
88
95
  toolCallId: request.toolCall.id,
89
96
  toolName: request.toolCall.name,
90
97
  toolInfo,
@@ -0,0 +1,9 @@
1
+ import type { PluginOptions } from "../../types.js";
2
+ export declare class AgentModeResolver {
3
+ private readonly options;
4
+ constructor(options: PluginOptions);
5
+ resolve(modeName?: string | null): {
6
+ name: string;
7
+ completionAdapter: import("../simpleAgent.js").AgentModeCompletionAdapter;
8
+ };
9
+ }
@@ -0,0 +1,9 @@
1
+ export class AgentModeResolver {
2
+ constructor(options) {
3
+ this.options = options;
4
+ }
5
+ resolve(modeName) {
6
+ var _a;
7
+ return (_a = this.options.modes.find((mode) => mode.name === modeName)) !== null && _a !== void 0 ? _a : this.options.modes[0];
8
+ }
9
+ }
@@ -0,0 +1,7 @@
1
+ import type { CompletionAdapter } from "adminforth";
2
+ import type { AgentTurnModels } from "../turn/turnTypes.js";
3
+ export declare class AgentModelFactory {
4
+ private readonly maxTokens;
5
+ constructor(maxTokens: number);
6
+ create(completionAdapter: CompletionAdapter): Promise<AgentTurnModels>;
7
+ }
@@ -0,0 +1,36 @@
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
+ import { createAgentChatModel } from "../simpleAgent.js";
11
+ export class AgentModelFactory {
12
+ constructor(maxTokens) {
13
+ this.maxTokens = maxTokens;
14
+ }
15
+ create(completionAdapter) {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ const [primaryModelSpec, summaryModelSpec] = yield Promise.all([
18
+ createAgentChatModel({
19
+ adapter: completionAdapter,
20
+ maxTokens: this.maxTokens,
21
+ purpose: "primary",
22
+ }),
23
+ createAgentChatModel({
24
+ adapter: completionAdapter,
25
+ maxTokens: this.maxTokens,
26
+ purpose: "summary",
27
+ }),
28
+ ]);
29
+ return {
30
+ model: primaryModelSpec.model,
31
+ summaryModel: summaryModelSpec.model,
32
+ modelMiddleware: primaryModelSpec.middleware,
33
+ };
34
+ });
35
+ }
36
+ }