@adminforth/agent 1.36.0 → 1.38.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.
Files changed (55) hide show
  1. package/agent/languageDetect.ts +0 -8
  2. package/agent/simpleAgent.ts +5 -5
  3. package/agent/systemPrompt.ts +35 -4
  4. package/agent/toolCallEvents.ts +31 -2
  5. package/agent/tools/apiTool.ts +1 -1
  6. package/agentResponseEvents.ts +197 -0
  7. package/apiBasedTools.ts +118 -284
  8. package/build.log +12 -2
  9. package/custom/ChatSurface.vue +31 -21
  10. package/custom/composables/agentAudio/agent-processing.mp3 +0 -0
  11. package/custom/composables/agentStore/constants.ts +8 -1
  12. package/custom/composables/agentStore/useAgentSessions.ts +88 -13
  13. package/custom/composables/useAgentAudio.ts +392 -0
  14. package/custom/composables/useAgentStore.ts +52 -5
  15. package/custom/conversation_area/ConversationArea.vue +1 -1
  16. package/custom/conversation_area/MessageRenderer.vue +12 -1
  17. package/custom/conversation_area/SystemMessageRenderer.vue +28 -0
  18. package/custom/conversation_area/TextRenderer.vue +4 -3
  19. package/custom/conversation_area/ToolRenderer.vue +1 -1
  20. package/custom/package.json +2 -1
  21. package/custom/pnpm-lock.yaml +29 -0
  22. package/custom/speech_recognition_frontend/AudioLines.vue +97 -0
  23. package/custom/speech_recognition_frontend/MicrophoneButon.vue +157 -0
  24. package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +22 -0
  25. package/custom/speech_recognition_frontend/voiceActivityDetection.ts +151 -0
  26. package/custom/types.ts +52 -2
  27. package/dist/agent/languageDetect.js +0 -6
  28. package/dist/agent/simpleAgent.js +4 -3
  29. package/dist/agent/systemPrompt.js +24 -3
  30. package/dist/agent/toolCallEvents.js +24 -2
  31. package/dist/agent/tools/apiTool.js +1 -1
  32. package/dist/agentResponseEvents.js +141 -0
  33. package/dist/apiBasedTools.js +95 -211
  34. package/dist/custom/ChatSurface.vue +31 -21
  35. package/dist/custom/composables/agentAudio/agent-processing.mp3 +0 -0
  36. package/dist/custom/composables/agentStore/constants.ts +8 -1
  37. package/dist/custom/composables/agentStore/useAgentSessions.ts +88 -13
  38. package/dist/custom/composables/useAgentAudio.ts +392 -0
  39. package/dist/custom/composables/useAgentStore.ts +52 -5
  40. package/dist/custom/conversation_area/ConversationArea.vue +1 -1
  41. package/dist/custom/conversation_area/MessageRenderer.vue +12 -1
  42. package/dist/custom/conversation_area/SystemMessageRenderer.vue +28 -0
  43. package/dist/custom/conversation_area/TextRenderer.vue +4 -3
  44. package/dist/custom/conversation_area/ToolRenderer.vue +1 -1
  45. package/dist/custom/package.json +2 -1
  46. package/dist/custom/pnpm-lock.yaml +29 -0
  47. package/dist/custom/speech_recognition_frontend/AudioLines.vue +97 -0
  48. package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +157 -0
  49. package/dist/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +22 -0
  50. package/dist/custom/speech_recognition_frontend/voiceActivityDetection.ts +151 -0
  51. package/dist/custom/types.ts +52 -2
  52. package/dist/index.js +290 -400
  53. package/index.ts +318 -492
  54. package/package.json +4 -2
  55. package/types.ts +1 -1
package/apiBasedTools.ts CHANGED
@@ -2,35 +2,26 @@ import {
2
2
  AdminForthDataTypes,
3
3
  logger,
4
4
  type AdminUser,
5
- type HttpExtra,
6
5
  type IAdminForth,
6
+ type IAdminForthHttpResponse,
7
7
  type IRegisteredApiSchema,
8
8
  } from 'adminforth';
9
9
  import dayjs from 'dayjs';
10
10
  import timezone from 'dayjs/plugin/timezone.js';
11
11
  import utc from 'dayjs/plugin/utc.js';
12
- import { inspect } from 'util';
13
12
  import YAML from 'yaml';
14
13
 
15
14
  dayjs.extend(utc);
16
15
  dayjs.extend(timezone);
17
16
 
18
- type CookieItem = {
19
- key: string;
20
- value: string;
21
- };
22
-
23
- type ToolOverrideCallParams = Pick<ApiBasedToolCallParams, 'httpExtra' | 'inputs' | 'userTimeZone'>;
24
17
 
25
18
  type ToolOverrideContext = {
26
19
  adminforth: IAdminForth;
27
20
  output?: unknown;
28
21
  adminUser?: AdminUser;
29
- httpExtra?: Partial<HttpExtra>;
30
22
  inputs?: Record<string, unknown>;
31
23
  resourceLabel?: string;
32
24
  userTimeZone?: string;
33
- invokeTool: (toolName: string, params?: ToolOverrideCallParams) => Promise<unknown>;
34
25
  };
35
26
 
36
27
  type ToolOverride = {
@@ -46,12 +37,16 @@ type GetResourceDataToolResponse = {
46
37
  };
47
38
 
48
39
  type DateTimeColumnType = AdminForthDataTypes.DATETIME | AdminForthDataTypes.TIME;
49
- type InternalApiOriginProvider = {
50
- getInternalApiOrigin?: () => string | undefined;
40
+ type RegisteredApiToolSchema = IRegisteredApiSchema & {
41
+ handler: (input: unknown) => void | Promise<unknown>;
51
42
  };
52
43
 
53
44
  const DEFAULT_USER_TIME_ZONE = 'UTC';
54
45
 
46
+ function hasRegisteredApiToolHandler(schema: IRegisteredApiSchema): schema is RegisteredApiToolSchema {
47
+ return typeof (schema as { handler?: unknown }).handler === 'function';
48
+ }
49
+
55
50
  function getInputString(inputs: Record<string, unknown> | undefined, key: string) {
56
51
  const value = inputs?.[key];
57
52
 
@@ -171,15 +166,14 @@ const TOOL_OVERRIDES: Record<string, ToolOverride> = {
171
166
  export type ApiBasedToolCallParams = {
172
167
  adminUser?: AdminUser;
173
168
  adminuser?: AdminUser;
169
+ abortSignal?: AbortSignal;
174
170
  inputs?: Record<string, unknown>;
175
- httpExtra?: Partial<HttpExtra>;
176
171
  userTimeZone?: string;
177
172
  };
178
173
 
179
174
  export type ApiBasedTool = {
180
175
  description?: string;
181
176
  input_schema?: unknown;
182
- input_schma?: unknown;
183
177
  output_schema?: unknown;
184
178
  call: (params?: ApiBasedToolCallParams) => Promise<string>;
185
179
  };
@@ -228,44 +222,6 @@ function sanitizeForYaml(
228
222
  return JSON.parse(serialized);
229
223
  }
230
224
 
231
- export function serializeUnknownError(error: unknown): Record<string, unknown> {
232
- if (error instanceof Error) {
233
- const errorWithCause = error as Error & { cause?: unknown };
234
- const errorRecord = error as unknown as Record<string, unknown>;
235
- const serialized: Record<string, unknown> = {
236
- name: error.name,
237
- message: error.message,
238
- stack: error.stack,
239
- };
240
-
241
- if (errorWithCause.cause !== undefined) {
242
- serialized.cause = serializeUnknownError(errorWithCause.cause);
243
- }
244
-
245
- for (const key of Object.getOwnPropertyNames(error)) {
246
- if (key in serialized) {
247
- continue;
248
- }
249
-
250
- serialized[key] = errorRecord[key];
251
- }
252
-
253
- return serialized;
254
- }
255
-
256
- if (typeof error === 'object' && error !== null) {
257
- return {
258
- type: error.constructor?.name ?? 'Object',
259
- inspected: inspect(error, { depth: 6, breakLength: 120 }),
260
- };
261
- }
262
-
263
- return {
264
- type: typeof error,
265
- value: error,
266
- };
267
- }
268
-
269
225
  function wipePath(target: unknown, pathParts: string[]): void {
270
226
  if (!target || typeof target !== 'object' || pathParts.length === 0) {
271
227
  return;
@@ -367,23 +323,12 @@ function formatDateTimeColumns(
367
323
  async function applyToolOverride(params: {
368
324
  adminforth: IAdminForth;
369
325
  adminUser?: AdminUser;
370
- httpExtra?: Partial<HttpExtra>;
371
326
  inputs?: Record<string, unknown>;
372
- invokeTool: (toolName: string, params?: ToolOverrideCallParams) => Promise<unknown>;
373
327
  output: unknown;
374
328
  toolName: string;
375
329
  userTimeZone?: string;
376
330
  }): Promise<unknown> {
377
- const {
378
- adminforth,
379
- adminUser,
380
- httpExtra,
381
- inputs,
382
- invokeTool,
383
- output,
384
- toolName,
385
- userTimeZone,
386
- } = params;
331
+ const { adminforth, adminUser, inputs, output, toolName, userTimeZone } = params;
387
332
  const sanitizedOutput = sanitizeForYaml(output);
388
333
  const override = TOOL_OVERRIDES[toolName];
389
334
 
@@ -395,41 +340,15 @@ async function applyToolOverride(params: {
395
340
  wipePath(sanitizedOutput, path.split('.'));
396
341
  }
397
342
 
398
- if (!override.post_process_response) {
399
- return sanitizedOutput;
400
- }
401
-
402
- const postProcessedOutput = await override.post_process_response({
403
- adminforth,
404
- output: sanitizedOutput,
405
- adminUser,
406
- httpExtra,
407
- inputs,
408
- userTimeZone,
409
- invokeTool: async (nestedToolName, nestedParams = {}) => {
410
- const nestedInputs = nestedParams.inputs ?? inputs;
411
- const nestedHttpExtra = nestedParams.httpExtra ?? httpExtra;
412
- const nestedUserTimeZone = nestedParams.userTimeZone ?? userTimeZone;
413
- const nestedOutput = await invokeTool(nestedToolName, {
414
- inputs: nestedInputs,
415
- httpExtra: nestedHttpExtra,
416
- userTimeZone: nestedUserTimeZone,
417
- });
418
-
419
- return applyToolOverride({
343
+ return override.post_process_response
344
+ ? sanitizeForYaml(await override.post_process_response({
420
345
  adminforth,
421
346
  adminUser,
422
- httpExtra: nestedHttpExtra,
423
- inputs: nestedInputs,
424
- invokeTool,
425
- output: nestedOutput,
426
- toolName: nestedToolName,
427
- userTimeZone: nestedUserTimeZone,
428
- });
429
- },
430
- });
431
-
432
- return sanitizeForYaml(postProcessedOutput);
347
+ output: sanitizedOutput,
348
+ inputs,
349
+ userTimeZone,
350
+ }))
351
+ : sanitizedOutput;
433
352
  }
434
353
 
435
354
  function endpointPathToToolName(path: string) {
@@ -465,40 +384,19 @@ function formatLogNameList(names: string[]) {
465
384
  export async function formatApiBasedToolCall(params: {
466
385
  adminforth: IAdminForth;
467
386
  adminUser?: AdminUser;
468
- httpExtra?: Partial<HttpExtra>;
469
387
  inputs?: Record<string, unknown>;
470
388
  toolName: string;
471
389
  userTimeZone?: string;
472
390
  }) {
473
- const formatTool = TOOL_OVERRIDES[params.toolName]?.format_tool;
474
-
475
- return await formatTool?.({
391
+ return await TOOL_OVERRIDES[params.toolName]?.format_tool?.({
476
392
  adminforth: params.adminforth,
477
393
  adminUser: params.adminUser,
478
- httpExtra: params.httpExtra,
479
394
  inputs: params.inputs,
480
395
  resourceLabel: resourceLabel(params.adminforth, params.inputs),
481
396
  userTimeZone: params.userTimeZone,
482
- invokeTool: async () => {
483
- throw new Error('Tool info formatting cannot invoke tools');
484
- },
485
397
  });
486
398
  }
487
399
 
488
- function normalizeCookies(
489
- cookies?: Partial<HttpExtra>['cookies'] | Record<string, string>,
490
- ): CookieItem[] {
491
- if (!cookies) {
492
- return [];
493
- }
494
-
495
- if (Array.isArray(cookies)) {
496
- return cookies;
497
- }
498
-
499
- return Object.entries(cookies).map(([key, value]) => ({ key, value }));
500
- }
501
-
502
400
  function normalizeDateTimeInputsToUtc(
503
401
  body: Record<string, unknown>,
504
402
  adminforth: IAdminForth,
@@ -532,10 +430,8 @@ function normalizeDateTimeInputsToUtc(
532
430
  return dayjs.tz(value, userTimeZone).utc().toISOString();
533
431
  }
534
432
 
535
- if (columnType === AdminForthDataTypes.TIME) {
536
- const userDate = dayjs().tz(userTimeZone).format('YYYY-MM-DD');
537
- return dayjs.tz(`${userDate}T${value}`, userTimeZone).utc().format('HH:mm:ss');
538
- }
433
+ const userDate = dayjs().tz(userTimeZone).format('YYYY-MM-DD');
434
+ return dayjs.tz(`${userDate}T${value}`, userTimeZone).utc().format('HH:mm:ss');
539
435
  };
540
436
 
541
437
  const normalizeValue = (value: unknown, key?: string): unknown => {
@@ -577,157 +473,115 @@ function normalizeDateTimeInputsToUtc(
577
473
  return normalizeValue(body) as Record<string, unknown>;
578
474
  }
579
475
 
580
- const METHODS_WITHOUT_REQUEST_BODY = new Set(['GET', 'HEAD']);
581
- const HEADERS_NOT_FORWARDED_TO_API_TOOL = new Set([
582
- 'connection',
583
- 'content-length',
584
- 'host',
585
- 'keep-alive',
586
- 'proxy-authenticate',
587
- 'proxy-authorization',
588
- 'te',
589
- 'trailer',
590
- 'transfer-encoding',
591
- 'upgrade',
592
- ]);
593
-
594
- function isAbsoluteHttpUrl(value: string) {
595
- try {
596
- const url = new URL(value);
597
- return url.protocol === 'http:' || url.protocol === 'https:';
598
- } catch {
599
- return false;
600
- }
601
- }
602
-
603
- function resolveOpenApiRequestUrl(params: {
604
- adminforth: IAdminForth;
605
- path: string;
606
- toolName: string;
607
- }) {
608
- const internalApiOrigin = (params.adminforth.express as InternalApiOriginProvider)
609
- .getInternalApiOrigin?.();
610
-
611
- if (internalApiOrigin) {
612
- const path = isAbsoluteHttpUrl(params.path)
613
- ? `${new URL(params.path).pathname}${new URL(params.path).search}`
614
- : params.path;
476
+ const METHODS_WITHOUT_REQUEST_BODY = new Set<string>(['GET', 'HEAD']);
615
477
 
616
- return new URL(path, internalApiOrigin).toString();
617
- }
478
+ function createDirectToolResponse(): IAdminForthHttpResponse & {
479
+ headers: Array<[string, string]>;
480
+ status: number;
481
+ message?: string;
482
+ } {
483
+ const headers: Array<[string, string]> = [];
618
484
 
619
- throw new Error(
620
- `Tool "${params.toolName}" cannot call OpenAPI path "${params.path}" because internal API origin is unavailable.`,
621
- );
485
+ return {
486
+ headers,
487
+ status: 200,
488
+ setHeader(name, value) {
489
+ headers.push([name, value]);
490
+ },
491
+ setStatus(code, message) {
492
+ this.status = code;
493
+ this.message = message;
494
+ },
495
+ blobStream() {
496
+ throw new Error('blobStream is not available for API-based agent tools');
497
+ },
498
+ };
622
499
  }
623
500
 
624
- function createToolRequestHeaders(
625
- httpExtra: Partial<HttpExtra> | undefined,
626
- userTimeZone?: string,
501
+ function validationErrorResponse(
502
+ error: 'REQUEST_VALIDATION_FAILED' | 'RESPONSE_VALIDATION_FAILED',
503
+ details: unknown,
627
504
  ) {
628
- const headers: Record<string, string> = {};
629
-
630
- for (const [name, value] of Object.entries(httpExtra?.headers ?? {})) {
631
- const headerName = name.toLowerCase();
632
-
633
- if (typeof value === 'string' && !HEADERS_NOT_FORWARDED_TO_API_TOOL.has(headerName)) {
634
- headers[headerName] = value;
635
- }
636
- }
637
-
638
- headers.accept = 'application/json';
639
- headers['content-type'] = 'application/json';
640
-
641
- if (userTimeZone) {
642
- headers['x-timezone'] = userTimeZone;
643
- }
644
-
645
- const cookieHeader = normalizeCookies(httpExtra?.cookies)
646
- .map(({ key, value }) => `${key}=${value}`)
647
- .join('; ');
648
-
649
- if (cookieHeader && !headers.cookie) {
650
- headers.cookie = cookieHeader;
651
- }
652
-
653
- return headers;
654
- }
655
-
656
- function appendInputsToQueryString(url: string, inputs: Record<string, unknown>) {
657
- const nextUrl = new URL(url);
658
-
659
- for (const [key, value] of Object.entries(inputs)) {
660
- if (value === undefined) {
661
- continue;
662
- }
663
-
664
- if (Array.isArray(value)) {
665
- for (const item of value) {
666
- nextUrl.searchParams.append(
667
- key,
668
- typeof item === 'object' && item !== null ? JSON.stringify(item) : String(item),
669
- );
670
- }
671
- continue;
672
- }
673
-
674
- nextUrl.searchParams.set(
675
- key,
676
- typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value),
677
- );
678
- }
679
-
680
- return nextUrl.toString();
681
- }
682
-
683
- async function parseOpenApiToolResponse(response: Response) {
684
- const responseText = await response.text();
685
- const payload = responseText && response.headers.get('content-type')?.includes('application/json')
686
- ? JSON.parse(responseText)
687
- : responseText;
688
-
689
- if (response.ok) {
690
- return responseText ? payload : { status: response.status };
691
- }
692
-
693
505
  return {
694
- error: 'HTTP_ERROR',
695
- status: response.status,
696
- statusText: response.statusText,
697
- response: payload,
506
+ error,
507
+ details,
698
508
  };
699
509
  }
700
510
 
701
511
  async function callOpenApiSchema(params: {
702
512
  adminforth: IAdminForth;
703
- httpExtra?: Partial<HttpExtra>;
513
+ adminUser?: AdminUser;
514
+ abortSignal?: AbortSignal;
704
515
  inputs?: Record<string, unknown>;
705
- schema: IRegisteredApiSchema;
516
+ schema: RegisteredApiToolSchema;
706
517
  toolName: string;
707
518
  userTimeZone?: string;
708
519
  }) {
709
- const { adminforth, httpExtra, inputs, schema, toolName, userTimeZone } = params;
520
+ const { adminforth, adminUser, abortSignal, inputs, schema, toolName, userTimeZone } = params;
710
521
  const method = schema.method.toUpperCase();
711
- const body = normalizeDateTimeInputsToUtc(
712
- (inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>,
522
+ const normalizedInputs = normalizeDateTimeInputsToUtc(
523
+ (inputs ?? {}) as Record<string, unknown>,
713
524
  adminforth,
714
525
  userTimeZone,
715
526
  );
716
- const requestUrl = resolveOpenApiRequestUrl({
717
- adminforth,
718
- path: schema.path,
719
- toolName,
720
- });
721
527
  const hasRequestBody = !METHODS_WITHOUT_REQUEST_BODY.has(method);
722
- logger.info(`Calling OpenAPI tool "${toolName}" with method ${method} at URL ${requestUrl}`);
723
- const response = await fetch(hasRequestBody ? requestUrl : appendInputsToQueryString(requestUrl, body), {
724
- method,
725
- headers: createToolRequestHeaders(httpExtra, userTimeZone),
726
- body: hasRequestBody ? JSON.stringify(body) : undefined,
528
+ const body = hasRequestBody ? normalizedInputs : {};
529
+ const query = hasRequestBody ? {} : normalizedInputs;
530
+ const requestValidation = adminforth.openApi.validateRequestSchema(schema, body);
531
+
532
+ if (!requestValidation.valid) {
533
+ return validationErrorResponse('REQUEST_VALIDATION_FAILED', requestValidation.errors);
534
+ }
535
+
536
+ const response = createDirectToolResponse();
537
+ logger.info(`Calling OpenAPI tool "${toolName}" with direct handler`);
538
+ const tr = (
539
+ msg: string,
540
+ category: string,
541
+ trParams: unknown,
542
+ pluralizationNumber?: number,
543
+ ) => adminforth.tr(msg, category, undefined, trParams, pluralizationNumber);
544
+ const output = await schema.handler({
545
+ body,
546
+ query,
547
+ headers: {},
548
+ cookies: [],
549
+ adminUser,
550
+ response,
551
+ requestUrl: schema.path,
552
+ abortSignal: abortSignal ?? new AbortController().signal,
553
+ _raw_express_req: undefined as never,
554
+ _raw_express_res: undefined as never,
555
+ tr,
727
556
  });
728
- logger.info(`Received response with status ${response.status} from OpenAPI tool "${toolName}"`);
729
557
 
730
- return parseOpenApiToolResponse(response);
558
+ if (response.message) {
559
+ return response.status >= 400
560
+ ? {
561
+ error: 'HANDLER_ERROR',
562
+ status: response.status,
563
+ response: response.message,
564
+ }
565
+ : response.message;
566
+ }
567
+
568
+ if (output === null) {
569
+ return { status: response.status };
570
+ }
571
+
572
+ const responseValidation = adminforth.openApi.validateResponseSchema(schema, output);
573
+
574
+ if (!responseValidation.valid) {
575
+ return validationErrorResponse('RESPONSE_VALIDATION_FAILED', responseValidation.errors);
576
+ }
577
+
578
+ return response.status >= 400
579
+ ? {
580
+ error: 'HANDLER_ERROR',
581
+ status: response.status,
582
+ response: output,
583
+ }
584
+ : output;
731
585
  }
732
586
 
733
587
  export function prepareApiBasedTools(
@@ -735,15 +589,15 @@ export function prepareApiBasedTools(
735
589
  hiddenResourceIds: Iterable<string> = [],
736
590
  ): Record<string, ApiBasedTool> {
737
591
  const apiBasedTools: Record<string, ApiBasedTool> = {};
738
- const openApiSchemas = adminforth.openApi.registeredSchemas.filter(
739
- (schema) => schema.request_schema || schema.response_schema,
740
- );
741
- const openApiSchemasByToolName = new Map<string, IRegisteredApiSchema>();
592
+ const openApiSchemas = adminforth.openApi.registeredSchemas;
593
+ const openApiSchemasByToolName = new Map<string, RegisteredApiToolSchema>();
742
594
  const hiddenResourceIdSet = new Set(hiddenResourceIds);
743
595
 
744
596
  for (const schema of openApiSchemas) {
745
597
  const toolName = openApiSchemaPathToToolName(schema.path, adminforth);
746
- openApiSchemasByToolName.set(toolName, schema);
598
+ if (hasRegisteredApiToolHandler(schema)) {
599
+ openApiSchemasByToolName.set(toolName, schema);
600
+ }
747
601
  }
748
602
 
749
603
  logger.info(
@@ -759,9 +613,8 @@ export function prepareApiBasedTools(
759
613
  apiBasedTools[toolName] = {
760
614
  description: schema.description,
761
615
  input_schema: schema.request_schema,
762
- input_schma: schema.request_schema,
763
616
  output_schema: schema.response_schema,
764
- call: async ({ adminUser, adminuser, inputs, httpExtra, userTimeZone } = {}) => {
617
+ call: async ({ adminUser, adminuser, abortSignal, inputs, userTimeZone } = {}) => {
765
618
  if (isHiddenResourceCall(hiddenResourceIdSet, inputs)) {
766
619
  return YAML.stringify({
767
620
  error: 'RESOURCE_NOT_AVAILABLE',
@@ -769,38 +622,20 @@ export function prepareApiBasedTools(
769
622
  });
770
623
  }
771
624
 
772
- const invokeTool = async (
773
- nextToolName: string,
774
- nextParams: ToolOverrideCallParams = {},
775
- ) => {
776
- const nextSchema = openApiSchemasByToolName.get(nextToolName);
777
-
778
- if (!nextSchema) {
779
- throw new Error(`Tool ${nextToolName} is not registered in OpenAPI`);
780
- }
781
-
782
- return callOpenApiSchema({
783
- adminforth,
784
- schema: nextSchema,
785
- toolName: nextToolName,
786
- inputs: nextParams.inputs,
787
- httpExtra: nextParams.httpExtra,
788
- userTimeZone: nextParams.userTimeZone,
789
- });
790
- };
791
-
792
- const output = await invokeTool(toolName, {
625
+ const output = await callOpenApiSchema({
626
+ adminforth,
627
+ adminUser: adminUser ?? adminuser,
628
+ abortSignal,
629
+ schema,
630
+ toolName,
793
631
  inputs,
794
- httpExtra,
795
632
  userTimeZone,
796
633
  });
797
634
 
798
635
  const processedOutput = await applyToolOverride({
799
636
  adminforth,
800
637
  adminUser: adminUser ?? adminuser,
801
- httpExtra,
802
638
  inputs,
803
- invokeTool,
804
639
  output,
805
640
  toolName,
806
641
  userTimeZone,
@@ -822,7 +657,6 @@ export function serializeApiBasedTool(tool: ApiBasedTool | undefined) {
822
657
  return {
823
658
  description: tool.description,
824
659
  input_schema: tool.input_schema,
825
- input_schma: tool.input_schma,
826
660
  output_schema: tool.output_schema,
827
661
  call: '[Function]',
828
662
  };
package/build.log CHANGED
@@ -15,8 +15,11 @@ custom/tsconfig.json
15
15
  custom/types.ts
16
16
  custom/utils.ts
17
17
  custom/composables/
18
+ custom/composables/useAgentAudio.ts
18
19
  custom/composables/useAgentStore.ts
19
20
  custom/composables/useAgentTransitions.ts
21
+ custom/composables/agentAudio/
22
+ custom/composables/agentAudio/agent-processing.mp3
20
23
  custom/composables/agentStore/
21
24
  custom/composables/agentStore/constants.ts
22
25
  custom/composables/agentStore/pageContext.ts
@@ -28,6 +31,7 @@ custom/conversation_area/ConversationArea.vue
28
31
  custom/conversation_area/MessageRenderer.vue
29
32
  custom/conversation_area/ProcessingTimeline.vue
30
33
  custom/conversation_area/ReasoningRenderer.vue
34
+ custom/conversation_area/SystemMessageRenderer.vue
31
35
  custom/conversation_area/TextRenderer.vue
32
36
  custom/conversation_area/ThreeDotsAnimation.vue
33
37
  custom/conversation_area/ToolRenderer.vue
@@ -46,6 +50,12 @@ custom/skills/fetch_data/
46
50
  custom/skills/fetch_data/SKILL.md
47
51
  custom/skills/mutate_data/
48
52
  custom/skills/mutate_data/SKILL.md
53
+ custom/speech_recognition_frontend/
54
+ custom/speech_recognition_frontend/AudioLines.vue
55
+ custom/speech_recognition_frontend/MicrophoneButon.vue
56
+ custom/speech_recognition_frontend/voiceActivityDetection.ts
57
+ custom/speech_recognition_frontend/types/
58
+ custom/speech_recognition_frontend/types/voice-activity-detection.d.ts
49
59
 
50
- sent 216,415 bytes received 699 bytes 434,228.00 bytes/sec
51
- total size is 213,565 speedup is 0.98
60
+ sent 1,655,972 bytes received 856 bytes 3,313,656.00 bytes/sec
61
+ total size is 1,652,100 speedup is 1.00
@@ -118,8 +118,12 @@
118
118
  transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
119
119
  }"
120
120
  >
121
- <div class="w-full border rounded-lg pb-8 dark:bg-gray-700">
121
+ <div
122
+ class="w-full border rounded-lg pb-8 dark:bg-gray-700"
123
+ :class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border'"
124
+ >
122
125
  <textarea
126
+ v-if="!agentStore.isAudioChatMode"
123
127
  v-model="agentStore.userMessageInput"
124
128
  ref="textInput"
125
129
  @input="autoResize"
@@ -169,26 +173,29 @@
169
173
  </button>
170
174
  </div>
171
175
  </div>
172
- <Button
173
- v-if="!agentStore.isResponseInProgress"
174
- class="absolute right-4 bottom-2 !p-0 h-9 w-9"
175
- @click="sendMessage"
176
- :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
177
- >
178
- <IconArrowUpOutline
179
- class="w-8 h-8 p-1
180
- text-white"
181
- />
182
- </Button>
183
- <Button
184
- v-else
185
- class="absolute right-4 bottom-2 !p-0 h-9 w-9"
186
- @click="stopCurrentRequest"
187
- >
188
- <div
189
- class="w-3 h-3 bg-white rounded-sm"
190
- />
191
- </Button>
176
+ <MicrophoneButton v-if="props.meta.hasAudioAdapter" />
177
+ <template v-if="!agentStore.isAudioChatMode">
178
+ <Button
179
+ v-if="!agentStore.isResponseInProgress"
180
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9 transition-opacity duration-200"
181
+ @click="sendMessage"
182
+ :disabled="!agentStore.trimmedUserMessage || agentStore.isResponseInProgress"
183
+ >
184
+ <IconArrowUpOutline
185
+ class="w-8 h-8 p-1
186
+ text-white"
187
+ />
188
+ </Button>
189
+ <Button
190
+ v-else
191
+ class="absolute right-4 bottom-2 !p-0 h-9 w-9"
192
+ @click="stopCurrentRequest"
193
+ >
194
+ <div
195
+ class="w-3 h-3 bg-white rounded-sm"
196
+ />
197
+ </Button>
198
+ </template>
192
199
  </div>
193
200
  </div>
194
201
  </div>
@@ -209,6 +216,7 @@ import { useAgentTransitions } from './composables/useAgentTransitions';
209
216
  import { Button } from '@/afcl';
210
217
  import { useCoreStore } from '@/stores/core';
211
218
  import { remToPx } from './utils';
219
+ import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
212
220
 
213
221
  const props = defineProps<{
214
222
  meta: {
@@ -218,6 +226,7 @@ const props = defineProps<{
218
226
  }>;
219
227
  defaultModeName: string | null;
220
228
  stickByDefault: boolean;
229
+ hasAudioAdapter: boolean;
221
230
  }
222
231
  adminUser: any
223
232
  }>();
@@ -321,6 +330,7 @@ function selectMode(modeName: string) {
321
330
  }
322
331
 
323
332
  async function sendMessage() {
333
+ if (agentStore.isAudioChatMode) return;
324
334
  isModeMenuOpen.value = false;
325
335
  await agentStore.sendMessage();
326
336
  autoResize();