@adminforth/agent 1.37.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.
- package/agent/languageDetect.ts +0 -8
- package/agent/simpleAgent.ts +5 -5
- package/agent/systemPrompt.ts +35 -4
- package/agent/toolCallEvents.ts +31 -2
- package/agent/tools/apiTool.ts +1 -1
- package/agentResponseEvents.ts +197 -0
- package/apiBasedTools.ts +118 -284
- package/build.log +12 -2
- package/custom/ChatSurface.vue +31 -21
- package/custom/composables/agentAudio/agent-processing.mp3 +0 -0
- package/custom/composables/agentStore/constants.ts +8 -1
- package/custom/composables/agentStore/useAgentSessions.ts +85 -12
- package/custom/composables/useAgentAudio.ts +392 -0
- package/custom/composables/useAgentStore.ts +52 -5
- package/custom/conversation_area/ConversationArea.vue +1 -1
- package/custom/conversation_area/MessageRenderer.vue +12 -1
- package/custom/conversation_area/SystemMessageRenderer.vue +28 -0
- package/custom/conversation_area/TextRenderer.vue +4 -3
- package/custom/conversation_area/ToolRenderer.vue +1 -1
- package/custom/package.json +2 -1
- package/custom/pnpm-lock.yaml +29 -0
- package/custom/speech_recognition_frontend/AudioLines.vue +97 -0
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +157 -0
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +22 -0
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +151 -0
- package/custom/types.ts +52 -2
- package/dist/agent/languageDetect.js +0 -6
- package/dist/agent/simpleAgent.js +4 -3
- package/dist/agent/systemPrompt.js +24 -3
- package/dist/agent/toolCallEvents.js +24 -2
- package/dist/agent/tools/apiTool.js +1 -1
- package/dist/agentResponseEvents.js +141 -0
- package/dist/apiBasedTools.js +95 -211
- package/dist/custom/ChatSurface.vue +31 -21
- package/dist/custom/composables/agentAudio/agent-processing.mp3 +0 -0
- package/dist/custom/composables/agentStore/constants.ts +8 -1
- package/dist/custom/composables/agentStore/useAgentSessions.ts +85 -12
- package/dist/custom/composables/useAgentAudio.ts +392 -0
- package/dist/custom/composables/useAgentStore.ts +52 -5
- package/dist/custom/conversation_area/ConversationArea.vue +1 -1
- package/dist/custom/conversation_area/MessageRenderer.vue +12 -1
- package/dist/custom/conversation_area/SystemMessageRenderer.vue +28 -0
- package/dist/custom/conversation_area/TextRenderer.vue +4 -3
- package/dist/custom/conversation_area/ToolRenderer.vue +1 -1
- package/dist/custom/package.json +2 -1
- package/dist/custom/pnpm-lock.yaml +29 -0
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +97 -0
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +157 -0
- package/dist/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +22 -0
- package/dist/custom/speech_recognition_frontend/voiceActivityDetection.ts +151 -0
- package/dist/custom/types.ts +52 -2
- package/dist/index.js +290 -400
- package/index.ts +318 -492
- package/package.json +3 -2
- 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
|
|
50
|
-
|
|
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
|
-
|
|
399
|
-
|
|
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
|
-
|
|
423
|
-
inputs
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
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
|
-
|
|
536
|
-
|
|
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
|
-
|
|
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
|
-
|
|
620
|
-
|
|
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
|
|
625
|
-
|
|
626
|
-
|
|
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
|
|
695
|
-
|
|
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
|
-
|
|
513
|
+
adminUser?: AdminUser;
|
|
514
|
+
abortSignal?: AbortSignal;
|
|
704
515
|
inputs?: Record<string, unknown>;
|
|
705
|
-
schema:
|
|
516
|
+
schema: RegisteredApiToolSchema;
|
|
706
517
|
toolName: string;
|
|
707
518
|
userTimeZone?: string;
|
|
708
519
|
}) {
|
|
709
|
-
const { adminforth,
|
|
520
|
+
const { adminforth, adminUser, abortSignal, inputs, schema, toolName, userTimeZone } = params;
|
|
710
521
|
const method = schema.method.toUpperCase();
|
|
711
|
-
const
|
|
712
|
-
(inputs ??
|
|
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
|
-
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
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
|
|
739
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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
|
|
51
|
-
total size is
|
|
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
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -118,8 +118,12 @@
|
|
|
118
118
|
transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
119
119
|
}"
|
|
120
120
|
>
|
|
121
|
-
<div
|
|
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
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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();
|
|
Binary file
|