@adminforth/agent 1.24.14 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/simpleAgent.ts +5 -0
- package/agent/tools/apiTool.ts +1 -0
- package/apiBasedTools.ts +209 -224
- package/build.log +2 -2
- package/custom/ChatSurface.vue +4 -2
- package/custom/CustomAutoScrollContainer.vue +1 -1
- package/custom/conversation_area/ConversationArea.vue +154 -13
- package/custom/conversation_area/TextRenderer.vue +1 -1
- package/custom/types.ts +0 -1
- package/dist/agent/simpleAgent.js +3 -1
- package/dist/agent/tools/apiTool.js +1 -0
- package/dist/apiBasedTools.js +147 -137
- package/dist/custom/ChatSurface.vue +4 -2
- package/dist/custom/CustomAutoScrollContainer.vue +1 -1
- package/dist/custom/conversation_area/ConversationArea.vue +154 -13
- package/dist/custom/conversation_area/TextRenderer.vue +1 -1
- package/dist/custom/types.ts +0 -1
- package/dist/index.js +16 -7
- package/index.ts +16 -7
- package/package.json +1 -1
package/agent/simpleAgent.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
logger,
|
|
5
5
|
type AdminUser,
|
|
6
6
|
type CompletionAdapter,
|
|
7
|
+
type HttpExtra,
|
|
7
8
|
type IAdminForth,
|
|
8
9
|
} from "adminforth";
|
|
9
10
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
@@ -24,6 +25,7 @@ export const contextSchema = z.object({
|
|
|
24
25
|
userTimeZone: z.string(),
|
|
25
26
|
sessionId: z.string(),
|
|
26
27
|
turnId: z.string(),
|
|
28
|
+
httpExtra: z.custom<Partial<HttpExtra>>().optional(),
|
|
27
29
|
emitToolCallEvent: z.custom<ToolCallEventSink>(),
|
|
28
30
|
});
|
|
29
31
|
|
|
@@ -229,6 +231,7 @@ export async function callAgent(params: {
|
|
|
229
231
|
customComponentsDir: string;
|
|
230
232
|
sessionId: string;
|
|
231
233
|
turnId: string;
|
|
234
|
+
httpExtra?: Partial<HttpExtra>;
|
|
232
235
|
userTimeZone: string;
|
|
233
236
|
emitToolCallEvent: ToolCallEventSink;
|
|
234
237
|
sequenceDebugSink: SequenceDebugModelCallSink;
|
|
@@ -246,6 +249,7 @@ export async function callAgent(params: {
|
|
|
246
249
|
customComponentsDir,
|
|
247
250
|
sessionId,
|
|
248
251
|
turnId,
|
|
252
|
+
httpExtra,
|
|
249
253
|
userTimeZone,
|
|
250
254
|
emitToolCallEvent,
|
|
251
255
|
sequenceDebugSink,
|
|
@@ -289,6 +293,7 @@ export async function callAgent(params: {
|
|
|
289
293
|
userTimeZone,
|
|
290
294
|
sessionId,
|
|
291
295
|
turnId,
|
|
296
|
+
httpExtra,
|
|
292
297
|
emitToolCallEvent,
|
|
293
298
|
},
|
|
294
299
|
});
|
package/agent/tools/apiTool.ts
CHANGED
|
@@ -51,6 +51,7 @@ export function createApiTool(toolName: string, apiBasedTool: ApiBasedTool) {
|
|
|
51
51
|
const normalizedInput = (input ?? {}) as Record<string, unknown>;
|
|
52
52
|
return apiBasedTool.call({
|
|
53
53
|
adminUser: runtime.context.adminUser,
|
|
54
|
+
httpExtra: runtime.context.httpExtra,
|
|
54
55
|
inputs: normalizedInput,
|
|
55
56
|
userTimeZone: runtime.context.userTimeZone,
|
|
56
57
|
});
|
package/apiBasedTools.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdminForthDataTypes,
|
|
3
|
+
logger,
|
|
3
4
|
type AdminUser,
|
|
4
5
|
type HttpExtra,
|
|
5
6
|
type IAdminForth,
|
|
6
|
-
type IAdminForthHttpResponse,
|
|
7
|
-
type IHttpServer,
|
|
8
7
|
type IRegisteredApiSchema,
|
|
9
8
|
} from 'adminforth';
|
|
10
9
|
import dayjs from 'dayjs';
|
|
11
10
|
import timezone from 'dayjs/plugin/timezone.js';
|
|
12
11
|
import utc from 'dayjs/plugin/utc.js';
|
|
13
|
-
import { PassThrough } from 'stream';
|
|
14
12
|
import { inspect } from 'util';
|
|
15
13
|
import YAML from 'yaml';
|
|
16
14
|
|
|
@@ -22,47 +20,6 @@ type CookieItem = {
|
|
|
22
20
|
value: string;
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
type CapturedEndpointHandlerInput = {
|
|
26
|
-
body: Record<string, unknown>;
|
|
27
|
-
adminUser?: AdminUser;
|
|
28
|
-
query: Record<string, string>;
|
|
29
|
-
headers: Record<string, any>;
|
|
30
|
-
cookies: CookieItem[];
|
|
31
|
-
response: IAdminForthHttpResponse;
|
|
32
|
-
requestUrl: string;
|
|
33
|
-
abortSignal: AbortSignal;
|
|
34
|
-
_raw_express_req: any;
|
|
35
|
-
_raw_express_res: any;
|
|
36
|
-
tr: (
|
|
37
|
-
msg: string,
|
|
38
|
-
category: string,
|
|
39
|
-
params: any,
|
|
40
|
-
pluralizationNumber?: number,
|
|
41
|
-
) => Promise<string>;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
type EndpointWithSchemas = {
|
|
45
|
-
method: string;
|
|
46
|
-
noAuth?: boolean;
|
|
47
|
-
path: string;
|
|
48
|
-
description?: string;
|
|
49
|
-
request_schema?: unknown;
|
|
50
|
-
response_schema?: unknown;
|
|
51
|
-
responce_schema?: unknown;
|
|
52
|
-
handler: (input: CapturedEndpointHandlerInput) => Promise<any> | any;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
type CapturedEndpoint = EndpointWithSchemas & {
|
|
56
|
-
normalizedResponseSchema?: unknown;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
type ToolHttpResponse = IAdminForthHttpResponse & {
|
|
60
|
-
headers: Array<[string, string]>;
|
|
61
|
-
jsonPayload?: unknown;
|
|
62
|
-
status: number;
|
|
63
|
-
message?: string;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
23
|
type ToolOverrideCallParams = Pick<ApiBasedToolCallParams, 'httpExtra' | 'inputs' | 'userTimeZone'>;
|
|
67
24
|
|
|
68
25
|
type ToolOverrideContext = {
|
|
@@ -388,9 +345,9 @@ function formatDateTimeColumns(
|
|
|
388
345
|
async function applyToolOverride(params: {
|
|
389
346
|
adminforth: IAdminForth;
|
|
390
347
|
adminUser?: AdminUser;
|
|
391
|
-
capturedEndpointsByToolName: Record<string, CapturedEndpoint>;
|
|
392
348
|
httpExtra?: Partial<HttpExtra>;
|
|
393
349
|
inputs?: Record<string, unknown>;
|
|
350
|
+
invokeTool: (toolName: string, params?: ToolOverrideCallParams) => Promise<unknown>;
|
|
394
351
|
output: unknown;
|
|
395
352
|
toolName: string;
|
|
396
353
|
userTimeZone?: string;
|
|
@@ -398,9 +355,9 @@ async function applyToolOverride(params: {
|
|
|
398
355
|
const {
|
|
399
356
|
adminforth,
|
|
400
357
|
adminUser,
|
|
401
|
-
capturedEndpointsByToolName,
|
|
402
358
|
httpExtra,
|
|
403
359
|
inputs,
|
|
360
|
+
invokeTool,
|
|
404
361
|
output,
|
|
405
362
|
toolName,
|
|
406
363
|
userTimeZone,
|
|
@@ -427,19 +384,10 @@ async function applyToolOverride(params: {
|
|
|
427
384
|
inputs,
|
|
428
385
|
userTimeZone,
|
|
429
386
|
invokeTool: async (nestedToolName, nestedParams = {}) => {
|
|
430
|
-
const nestedEndpoint = capturedEndpointsByToolName[nestedToolName];
|
|
431
|
-
|
|
432
|
-
if (!nestedEndpoint) {
|
|
433
|
-
throw new Error(`Tool ${nestedToolName} is not registered`);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
387
|
const nestedInputs = nestedParams.inputs ?? inputs;
|
|
437
388
|
const nestedHttpExtra = nestedParams.httpExtra ?? httpExtra;
|
|
438
389
|
const nestedUserTimeZone = nestedParams.userTimeZone ?? userTimeZone;
|
|
439
|
-
const nestedOutput = await
|
|
440
|
-
adminforth,
|
|
441
|
-
endpoint: nestedEndpoint,
|
|
442
|
-
adminUser,
|
|
390
|
+
const nestedOutput = await invokeTool(nestedToolName, {
|
|
443
391
|
inputs: nestedInputs,
|
|
444
392
|
httpExtra: nestedHttpExtra,
|
|
445
393
|
userTimeZone: nestedUserTimeZone,
|
|
@@ -448,9 +396,9 @@ async function applyToolOverride(params: {
|
|
|
448
396
|
return applyToolOverride({
|
|
449
397
|
adminforth,
|
|
450
398
|
adminUser,
|
|
451
|
-
capturedEndpointsByToolName,
|
|
452
399
|
httpExtra: nestedHttpExtra,
|
|
453
400
|
inputs: nestedInputs,
|
|
401
|
+
invokeTool,
|
|
454
402
|
output: nestedOutput,
|
|
455
403
|
toolName: nestedToolName,
|
|
456
404
|
userTimeZone: nestedUserTimeZone,
|
|
@@ -487,6 +435,10 @@ function openApiSchemaPathToToolName(path: string, adminforth: IAdminForth) {
|
|
|
487
435
|
return endpointPathToToolName(stripAdminApiPrefix(path, adminforth));
|
|
488
436
|
}
|
|
489
437
|
|
|
438
|
+
function formatLogNameList(names: string[]) {
|
|
439
|
+
return names.length ? names.join(', ') : '(none)';
|
|
440
|
+
}
|
|
441
|
+
|
|
490
442
|
export async function formatApiBasedToolCall(params: {
|
|
491
443
|
adminforth: IAdminForth;
|
|
492
444
|
adminUser?: AdminUser;
|
|
@@ -523,84 +475,6 @@ function normalizeCookies(
|
|
|
523
475
|
return Object.entries(cookies).map(([key, value]) => ({ key, value }));
|
|
524
476
|
}
|
|
525
477
|
|
|
526
|
-
function createToolResponse(baseResponse?: IAdminForthHttpResponse): ToolHttpResponse {
|
|
527
|
-
return {
|
|
528
|
-
headers: [],
|
|
529
|
-
status: 200,
|
|
530
|
-
message: undefined,
|
|
531
|
-
setHeader(name, value) {
|
|
532
|
-
this.headers.push([name, value]);
|
|
533
|
-
baseResponse?.setHeader(name, value);
|
|
534
|
-
},
|
|
535
|
-
setStatus(code, message) {
|
|
536
|
-
this.status = code;
|
|
537
|
-
this.message = message;
|
|
538
|
-
baseResponse?.setStatus(code, message);
|
|
539
|
-
},
|
|
540
|
-
blobStream() {
|
|
541
|
-
return baseResponse?.blobStream() ?? new PassThrough();
|
|
542
|
-
},
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function createRawExpressRequest(params: {
|
|
547
|
-
adminUser?: AdminUser;
|
|
548
|
-
body: Record<string, unknown>;
|
|
549
|
-
cookies: CookieItem[];
|
|
550
|
-
headers: Record<string, any>;
|
|
551
|
-
method: string;
|
|
552
|
-
query: Record<string, string>;
|
|
553
|
-
requestUrl: string;
|
|
554
|
-
}) {
|
|
555
|
-
const cookieHeader = params.cookies
|
|
556
|
-
.map(({ key, value }) => `${key}=${value}`)
|
|
557
|
-
.join('; ');
|
|
558
|
-
|
|
559
|
-
return {
|
|
560
|
-
adminUser: params.adminUser,
|
|
561
|
-
body: params.body,
|
|
562
|
-
destroyed: false,
|
|
563
|
-
headers: {
|
|
564
|
-
...params.headers,
|
|
565
|
-
...(cookieHeader ? { cookie: cookieHeader } : {}),
|
|
566
|
-
},
|
|
567
|
-
method: params.method.toUpperCase(),
|
|
568
|
-
on: () => undefined,
|
|
569
|
-
query: params.query,
|
|
570
|
-
url: params.requestUrl,
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function createRawExpressResponse(response: ToolHttpResponse) {
|
|
575
|
-
const rawResponse = {
|
|
576
|
-
destroyed: false,
|
|
577
|
-
on: () => undefined,
|
|
578
|
-
setHeader(name: string, value: string) {
|
|
579
|
-
response.setHeader(name, value);
|
|
580
|
-
return rawResponse;
|
|
581
|
-
},
|
|
582
|
-
status(code: number) {
|
|
583
|
-
response.status = code;
|
|
584
|
-
return rawResponse;
|
|
585
|
-
},
|
|
586
|
-
send(message: string) {
|
|
587
|
-
response.message = message;
|
|
588
|
-
return rawResponse;
|
|
589
|
-
},
|
|
590
|
-
json(payload: unknown) {
|
|
591
|
-
response.jsonPayload = payload;
|
|
592
|
-
response.message = JSON.stringify(payload);
|
|
593
|
-
return rawResponse;
|
|
594
|
-
},
|
|
595
|
-
write: () => true,
|
|
596
|
-
writeHead: () => rawResponse,
|
|
597
|
-
writableEnded: false,
|
|
598
|
-
end: () => rawResponse,
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
return rawResponse;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
478
|
function normalizeDateTimeInputsToUtc(
|
|
605
479
|
body: Record<string, unknown>,
|
|
606
480
|
adminforth: IAdminForth,
|
|
@@ -679,100 +553,192 @@ function normalizeDateTimeInputsToUtc(
|
|
|
679
553
|
return normalizeValue(body) as Record<string, unknown>;
|
|
680
554
|
}
|
|
681
555
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
556
|
+
const METHODS_WITHOUT_REQUEST_BODY = new Set(['GET', 'HEAD']);
|
|
557
|
+
const HEADERS_NOT_FORWARDED_TO_API_TOOL = new Set([
|
|
558
|
+
'connection',
|
|
559
|
+
'content-length',
|
|
560
|
+
'host',
|
|
561
|
+
'keep-alive',
|
|
562
|
+
'proxy-authenticate',
|
|
563
|
+
'proxy-authorization',
|
|
564
|
+
'te',
|
|
565
|
+
'trailer',
|
|
566
|
+
'transfer-encoding',
|
|
567
|
+
'upgrade',
|
|
568
|
+
]);
|
|
569
|
+
|
|
570
|
+
function getHeaderValue(
|
|
571
|
+
headers: Partial<HttpExtra>['headers'] | undefined,
|
|
572
|
+
headerName: string,
|
|
573
|
+
) {
|
|
574
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
575
|
+
const value = Object.entries(headers ?? {}).find(
|
|
576
|
+
([name]) => name.toLowerCase() === normalizedHeaderName,
|
|
577
|
+
)?.[1];
|
|
578
|
+
|
|
579
|
+
if (typeof value !== 'string') {
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return value.split(',')[0].trim();
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function isAbsoluteHttpUrl(value: string) {
|
|
587
|
+
try {
|
|
588
|
+
const url = new URL(value);
|
|
589
|
+
return url.protocol === 'http:' || url.protocol === 'https:';
|
|
590
|
+
} catch {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function getRequestOrigin(httpExtra?: Partial<HttpExtra>) {
|
|
596
|
+
const requestUrl = httpExtra?.requestUrl;
|
|
597
|
+
|
|
598
|
+
if (requestUrl && isAbsoluteHttpUrl(requestUrl)) {
|
|
599
|
+
return new URL(requestUrl).origin;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const host = getHeaderValue(httpExtra?.headers, 'x-forwarded-host')
|
|
603
|
+
?? getHeaderValue(httpExtra?.headers, 'host');
|
|
604
|
+
|
|
605
|
+
if (!host) {
|
|
606
|
+
return undefined;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const protocol = getHeaderValue(httpExtra?.headers, 'x-forwarded-proto') ?? 'http';
|
|
610
|
+
return `${protocol}://${host}`;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function resolveOpenApiRequestUrl(params: {
|
|
686
614
|
httpExtra?: Partial<HttpExtra>;
|
|
687
|
-
|
|
688
|
-
|
|
615
|
+
path: string;
|
|
616
|
+
toolName: string;
|
|
689
617
|
}) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
headers,
|
|
711
|
-
method: endpoint.method,
|
|
712
|
-
query,
|
|
713
|
-
requestUrl,
|
|
714
|
-
});
|
|
715
|
-
const rawResponse = createRawExpressResponse(response);
|
|
716
|
-
const acceptLanguage = headers['accept-language'];
|
|
717
|
-
const tr = (
|
|
718
|
-
msg: string,
|
|
719
|
-
category: string = 'default',
|
|
720
|
-
translationParams: any,
|
|
721
|
-
pluralizationNumber?: number,
|
|
722
|
-
) => adminforth.tr(msg, category, acceptLanguage, translationParams, pluralizationNumber);
|
|
723
|
-
|
|
724
|
-
const output = await endpoint.handler({
|
|
725
|
-
body,
|
|
726
|
-
adminUser,
|
|
727
|
-
query,
|
|
728
|
-
headers,
|
|
729
|
-
cookies,
|
|
730
|
-
response,
|
|
731
|
-
requestUrl,
|
|
732
|
-
abortSignal: abortController.signal,
|
|
733
|
-
_raw_express_req: rawRequest,
|
|
734
|
-
_raw_express_res: rawResponse,
|
|
735
|
-
tr,
|
|
736
|
-
});
|
|
618
|
+
if (isAbsoluteHttpUrl(params.path)) {
|
|
619
|
+
return params.path;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const origin = getRequestOrigin(params.httpExtra);
|
|
623
|
+
|
|
624
|
+
if (!origin) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
`Tool "${params.toolName}" has relative OpenAPI path "${params.path}" but request host header is unavailable.`,
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return new URL(params.path, origin).toString();
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function createToolRequestHeaders(
|
|
634
|
+
httpExtra: Partial<HttpExtra> | undefined,
|
|
635
|
+
userTimeZone?: string,
|
|
636
|
+
) {
|
|
637
|
+
const headers: Record<string, string> = {};
|
|
737
638
|
|
|
738
|
-
|
|
739
|
-
|
|
639
|
+
for (const [name, value] of Object.entries(httpExtra?.headers ?? {})) {
|
|
640
|
+
const headerName = name.toLowerCase();
|
|
641
|
+
|
|
642
|
+
if (typeof value === 'string' && !HEADERS_NOT_FORWARDED_TO_API_TOOL.has(headerName)) {
|
|
643
|
+
headers[headerName] = value;
|
|
644
|
+
}
|
|
740
645
|
}
|
|
741
646
|
|
|
742
|
-
|
|
743
|
-
|
|
647
|
+
headers.accept = 'application/json';
|
|
648
|
+
headers['content-type'] = 'application/json';
|
|
649
|
+
|
|
650
|
+
if (userTimeZone) {
|
|
651
|
+
headers['x-timezone'] = userTimeZone;
|
|
744
652
|
}
|
|
745
653
|
|
|
746
|
-
|
|
747
|
-
|
|
654
|
+
const cookieHeader = normalizeCookies(httpExtra?.cookies)
|
|
655
|
+
.map(({ key, value }) => `${key}=${value}`)
|
|
656
|
+
.join('; ');
|
|
657
|
+
|
|
658
|
+
if (cookieHeader && !headers.cookie) {
|
|
659
|
+
headers.cookie = cookieHeader;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return headers;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function appendInputsToQueryString(url: string, inputs: Record<string, unknown>) {
|
|
666
|
+
const nextUrl = new URL(url);
|
|
667
|
+
|
|
668
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
669
|
+
if (value === undefined) {
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (Array.isArray(value)) {
|
|
674
|
+
for (const item of value) {
|
|
675
|
+
nextUrl.searchParams.append(
|
|
676
|
+
key,
|
|
677
|
+
typeof item === 'object' && item !== null ? JSON.stringify(item) : String(item),
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
nextUrl.searchParams.set(
|
|
684
|
+
key,
|
|
685
|
+
typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value),
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return nextUrl.toString();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async function parseOpenApiToolResponse(response: Response) {
|
|
693
|
+
const responseText = await response.text();
|
|
694
|
+
const payload = responseText && response.headers.get('content-type')?.includes('application/json')
|
|
695
|
+
? JSON.parse(responseText)
|
|
696
|
+
: responseText;
|
|
697
|
+
|
|
698
|
+
if (response.ok) {
|
|
699
|
+
return responseText ? payload : { status: response.status };
|
|
748
700
|
}
|
|
749
701
|
|
|
750
702
|
return {
|
|
751
|
-
|
|
703
|
+
error: 'HTTP_ERROR',
|
|
752
704
|
status: response.status,
|
|
705
|
+
statusText: response.statusText,
|
|
706
|
+
response: payload,
|
|
753
707
|
};
|
|
754
708
|
}
|
|
755
709
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}) as
|
|
768
|
-
|
|
710
|
+
async function callOpenApiSchema(params: {
|
|
711
|
+
adminforth: IAdminForth;
|
|
712
|
+
httpExtra?: Partial<HttpExtra>;
|
|
713
|
+
inputs?: Record<string, unknown>;
|
|
714
|
+
schema: IRegisteredApiSchema;
|
|
715
|
+
toolName: string;
|
|
716
|
+
userTimeZone?: string;
|
|
717
|
+
}) {
|
|
718
|
+
const { adminforth, httpExtra, inputs, schema, toolName, userTimeZone } = params;
|
|
719
|
+
const method = schema.method.toUpperCase();
|
|
720
|
+
const body = normalizeDateTimeInputsToUtc(
|
|
721
|
+
(inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>,
|
|
722
|
+
adminforth,
|
|
723
|
+
userTimeZone,
|
|
724
|
+
);
|
|
725
|
+
const requestUrl = resolveOpenApiRequestUrl({
|
|
726
|
+
httpExtra,
|
|
727
|
+
path: schema.path,
|
|
728
|
+
toolName,
|
|
729
|
+
});
|
|
730
|
+
const hasRequestBody = !METHODS_WITHOUT_REQUEST_BODY.has(method);
|
|
731
|
+
const response = await fetch(hasRequestBody ? requestUrl : appendInputsToQueryString(requestUrl, body), {
|
|
732
|
+
method,
|
|
733
|
+
headers: createToolRequestHeaders(httpExtra, userTimeZone),
|
|
734
|
+
body: hasRequestBody ? JSON.stringify(body) : undefined,
|
|
735
|
+
});
|
|
769
736
|
|
|
770
|
-
|
|
737
|
+
return parseOpenApiToolResponse(response);
|
|
738
|
+
}
|
|
771
739
|
|
|
740
|
+
export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, ApiBasedTool> {
|
|
772
741
|
const apiBasedTools: Record<string, ApiBasedTool> = {};
|
|
773
|
-
const capturedEndpointsByToolName = Object.fromEntries(
|
|
774
|
-
capturedEndpoints.map((endpoint) => [endpointPathToToolName(endpoint.path), endpoint]),
|
|
775
|
-
);
|
|
776
742
|
const openApiSchemas = adminforth.openApi.registeredSchemas.filter(
|
|
777
743
|
(schema) => schema.request_schema || schema.response_schema,
|
|
778
744
|
);
|
|
@@ -783,24 +749,43 @@ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, Ap
|
|
|
783
749
|
openApiSchemasByToolName.set(toolName, schema);
|
|
784
750
|
}
|
|
785
751
|
|
|
752
|
+
logger.info(
|
|
753
|
+
`AdminForth Agent OpenAPI APIs: ${formatLogNameList(
|
|
754
|
+
adminforth.openApi.registeredSchemas.map((schema) => openApiSchemaPathToToolName(schema.path, adminforth)),
|
|
755
|
+
)}`,
|
|
756
|
+
);
|
|
757
|
+
logger.info(
|
|
758
|
+
`AdminForth Agent OpenAPI tools connected: ${formatLogNameList([...openApiSchemasByToolName.keys()])}`,
|
|
759
|
+
);
|
|
760
|
+
|
|
786
761
|
for (const [toolName, schema] of openApiSchemasByToolName.entries()) {
|
|
787
|
-
const endpoint = capturedEndpointsByToolName[toolName];
|
|
788
762
|
apiBasedTools[toolName] = {
|
|
789
763
|
description: schema.description,
|
|
790
764
|
input_schema: schema.request_schema,
|
|
791
765
|
input_schma: schema.request_schema,
|
|
792
766
|
output_schema: schema.response_schema,
|
|
793
767
|
call: async ({ adminUser, adminuser, inputs, httpExtra, userTimeZone } = {}) => {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
768
|
+
const invokeTool = async (
|
|
769
|
+
nextToolName: string,
|
|
770
|
+
nextParams: ToolOverrideCallParams = {},
|
|
771
|
+
) => {
|
|
772
|
+
const nextSchema = openApiSchemasByToolName.get(nextToolName);
|
|
773
|
+
|
|
774
|
+
if (!nextSchema) {
|
|
775
|
+
throw new Error(`Tool ${nextToolName} is not registered in OpenAPI`);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return callOpenApiSchema({
|
|
779
|
+
adminforth,
|
|
780
|
+
schema: nextSchema,
|
|
781
|
+
toolName: nextToolName,
|
|
782
|
+
inputs: nextParams.inputs,
|
|
783
|
+
httpExtra: nextParams.httpExtra,
|
|
784
|
+
userTimeZone: nextParams.userTimeZone,
|
|
785
|
+
});
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
const output = await invokeTool(toolName, {
|
|
804
789
|
inputs,
|
|
805
790
|
httpExtra,
|
|
806
791
|
userTimeZone,
|
|
@@ -809,9 +794,9 @@ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, Ap
|
|
|
809
794
|
const processedOutput = await applyToolOverride({
|
|
810
795
|
adminforth,
|
|
811
796
|
adminUser: adminUser ?? adminuser,
|
|
812
|
-
capturedEndpointsByToolName,
|
|
813
797
|
httpExtra,
|
|
814
798
|
inputs,
|
|
799
|
+
invokeTool,
|
|
815
800
|
output,
|
|
816
801
|
toolName,
|
|
817
802
|
userTimeZone,
|
package/build.log
CHANGED
|
@@ -38,5 +38,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
38
38
|
custom/skills/mutate_data/
|
|
39
39
|
custom/skills/mutate_data/SKILL.md
|
|
40
40
|
|
|
41
|
-
sent
|
|
42
|
-
total size is
|
|
41
|
+
sent 206,204 bytes received 562 bytes 413,532.00 bytes/sec
|
|
42
|
+
total size is 203,890 speedup is 0.99
|
package/custom/ChatSurface.vue
CHANGED
|
@@ -93,7 +93,8 @@
|
|
|
93
93
|
<div
|
|
94
94
|
class="relative flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
95
95
|
>
|
|
96
|
-
<ConversationArea
|
|
96
|
+
<ConversationArea
|
|
97
|
+
ref="conversationArea"
|
|
97
98
|
v-if="agentStore.isChatOpen"
|
|
98
99
|
:messages="agentStore.chatMessages"
|
|
99
100
|
/>
|
|
@@ -201,6 +202,7 @@ const props = defineProps<{
|
|
|
201
202
|
const chatSurface = useTemplateRef('chatSurface');
|
|
202
203
|
const textInput = useTemplateRef('textInput');
|
|
203
204
|
const modeMenu = useTemplateRef('modeMenu');
|
|
205
|
+
const conversationArea = useTemplateRef('conversationArea');
|
|
204
206
|
const agentStore = useAgentStore();
|
|
205
207
|
const agentTransitions = useAgentTransitions();
|
|
206
208
|
const coreStore = useCoreStore();
|
|
@@ -254,7 +256,6 @@ onMounted(async () => {
|
|
|
254
256
|
agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
|
|
255
257
|
}
|
|
256
258
|
await agentStore.fetchSessionsList();
|
|
257
|
-
agentStore.setFullScreen(true);
|
|
258
259
|
});
|
|
259
260
|
|
|
260
261
|
function autoResize() {
|
|
@@ -286,6 +287,7 @@ async function sendMessage() {
|
|
|
286
287
|
isModeMenuOpen.value = false;
|
|
287
288
|
await agentStore.sendMessage();
|
|
288
289
|
autoResize();
|
|
290
|
+
conversationArea.value?.handleSendMessage();
|
|
289
291
|
}
|
|
290
292
|
|
|
291
293
|
</script>
|