@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.
@@ -34,9 +34,10 @@
34
34
  >
35
35
 
36
36
  <div
37
- v-for="(message, index) in props.messages" :key="message.id"
37
+ v-for="(message, index) in props.messages" :key="index"
38
38
  class="flex flex-col w-full mt-2"
39
39
  :class="message.role === 'user' ? 'self-end' : 'self-start'"
40
+ ref="messagesRefs"
40
41
  >
41
42
  <MessageRenderer :message="message" :isLastMessageInChat="index === props.messages.length - 1"/>
42
43
  </div>
@@ -50,7 +51,7 @@
50
51
  <p>{{ $t('Start the conversation') }}</p>
51
52
  <p class="tracking-normal text-base text">{{ $t('Give any input to begin') }}</p>
52
53
  </div>
53
- <div></div>
54
+ <div v-if="showBottomSpacer" class="w-full" :style="{ height: spacerHeight + 'px' }"></div>
54
55
  </CustomAutoScrollContainer>
55
56
  <button @click="scrollContainer.scrollToBottom();">
56
57
  <IconArrowDownOutline
@@ -64,8 +65,8 @@
64
65
 
65
66
 
66
67
  <script setup lang="ts">
67
- import type { IMessage, IPart } from '../types';
68
- import { useTemplateRef, ref, defineAsyncComponent, onMounted, onUnmounted, watch, computed, nextTick } from 'vue';
68
+ import type { IMessage } from '../types';
69
+ import { useTemplateRef, ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
69
70
  import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
70
71
  import SessionsHistory from '../SessionsHistory.vue';
71
72
  import { useAgentStore } from '../composables/useAgentStore';
@@ -77,18 +78,140 @@ const props = defineProps<{
77
78
  messages: IMessage[]
78
79
  }>();
79
80
 
81
+ defineExpose({
82
+ handleSendMessage
83
+ });
84
+
80
85
  const scrollContainer = useTemplateRef('scrollContainer');
81
86
  const showScrollToBottomButton = ref(false);
82
- const innerScrollContainerRef = ref(null);
87
+ const innerScrollContainerRef = ref<HTMLElement | null>(null);
83
88
  const agentStore = useAgentStore();
84
89
  const agentTransitions = useAgentTransitions();
85
90
  const showScrollContainer = ref(true);
86
- const chatContainerRef = ref(null);
91
+ const chatContainerRef = ref<HTMLElement | null>(null);
87
92
 
88
- const scrollHeight = computed(() => {
89
- return scrollContainer.value ? scrollContainer.value.scrollParams.scrollHeight : 0;
93
+ const messagesRefs = ref<Array<HTMLElement | null>>([]);
94
+ const showBottomSpacer = ref(false);
95
+ const spacerHeight = ref(0);
96
+ const MASK_HEIGHT = 20;
97
+ const EMPTY_MESSAGE_HEIGHT = 18;
98
+ let messageResizeObserver: ResizeObserver | null = null;
99
+ let observedLastUserMessageElement: HTMLElement | null = null;
100
+ let observedLastAgentMessageElement: HTMLElement | null = null;
101
+
102
+ function resetSpacer() {
103
+ showBottomSpacer.value = false;
104
+ spacerHeight.value = 0;
105
+ }
106
+
107
+ watch(() => agentStore.activeSessionId, () => {
108
+ resetSpacer();
90
109
  });
91
110
 
111
+ function getLastMessageElement(role: 'user' | 'assistant') {
112
+ const lastMessageIndex = props.messages.findLastIndex((message: IMessage) => message.role === role);
113
+ return messagesRefs.value[lastMessageIndex] ?? null;
114
+ }
115
+
116
+ function getHeightOfLastUserMessage() {
117
+ return getLastMessageElement('user')?.clientHeight ?? 0;
118
+ }
119
+
120
+ function getHeightOfLastAgentMessage() {
121
+ return getLastMessageElement('assistant')?.clientHeight ?? 0;
122
+ }
123
+
124
+ function getScrollClientHeight() {
125
+ return scrollContainer.value?.container.scrollEl.clientHeight ?? scrollContainer.value?.scrollParams.clientHeight ?? 0;
126
+ }
127
+
128
+ async function waitForRealHeight(role: 'user' | 'assistant'): Promise<number> {
129
+ return new Promise((resolve) => {
130
+ const interval = setInterval(() => {
131
+ const height = role === 'user' ? getHeightOfLastUserMessage() : getHeightOfLastAgentMessage();
132
+
133
+ if (height > EMPTY_MESSAGE_HEIGHT) {
134
+ clearInterval(interval);
135
+ resolve(height);
136
+ }
137
+ }, 50);
138
+ });
139
+ }
140
+
141
+ const useWaitingForHeight = ref(false);
142
+ async function updateSpacerHeight() {
143
+ if (!showBottomSpacer.value) {
144
+ return;
145
+ }
146
+
147
+ const clientHeight = getScrollClientHeight();
148
+
149
+ if (!clientHeight) {
150
+ return;
151
+ }
152
+
153
+ const lastUserMessageHeight = useWaitingForHeight.value ? await waitForRealHeight('user') : getHeightOfLastUserMessage();
154
+ const lastAgentMessageHeight = useWaitingForHeight.value ? await waitForRealHeight('assistant') : getHeightOfLastAgentMessage();
155
+
156
+ spacerHeight.value = Math.max(0, clientHeight - (lastUserMessageHeight + MASK_HEIGHT + lastAgentMessageHeight));
157
+ }
158
+
159
+ function stopObservingLastMessages() {
160
+ if (!messageResizeObserver) {
161
+ return;
162
+ }
163
+
164
+ if (observedLastUserMessageElement) {
165
+ messageResizeObserver.unobserve(observedLastUserMessageElement);
166
+ observedLastUserMessageElement = null;
167
+ }
168
+
169
+ if (observedLastAgentMessageElement) {
170
+ messageResizeObserver.unobserve(observedLastAgentMessageElement);
171
+ observedLastAgentMessageElement = null;
172
+ }
173
+ }
174
+
175
+ function observeLastMessages() {
176
+ if (!messageResizeObserver) {
177
+ return;
178
+ }
179
+
180
+ stopObservingLastMessages();
181
+
182
+ observedLastUserMessageElement = getLastMessageElement('user');
183
+ observedLastAgentMessageElement = getLastMessageElement('assistant');
184
+
185
+ if (observedLastUserMessageElement) {
186
+ messageResizeObserver.observe(observedLastUserMessageElement);
187
+ }
188
+
189
+ if (observedLastAgentMessageElement) {
190
+ messageResizeObserver.observe(observedLastAgentMessageElement);
191
+ }
192
+ }
193
+
194
+ async function refreshSpacerTracking() {
195
+ await nextTick();
196
+ observeLastMessages();
197
+ await updateSpacerHeight();
198
+ }
199
+
200
+ async function handleSendMessage() {
201
+ const clientHeight = getScrollClientHeight();
202
+
203
+ if (clientHeight) {
204
+ showBottomSpacer.value = true;
205
+ useWaitingForHeight.value = true;
206
+ setTimeout(() => {
207
+ useWaitingForHeight.value = false;
208
+ }, 1000);
209
+ await updateSpacerHeight();
210
+ await nextTick();
211
+ scrollContainer.value?.scrollToBottom();
212
+ }
213
+ }
214
+
92
215
  function recalculateScroll() {
93
216
  if (scrollContainer.value) {
94
217
  scrollContainer.value.handleScroll(false);
@@ -101,26 +224,44 @@ watch(() => agentStore.activeSessionId, async () => {
101
224
  showScrollContainer.value = false;
102
225
  await nextTick();
103
226
  showScrollContainer.value = true;
104
- await nextTick();
227
+ await refreshSpacerTracking();
105
228
  recalculateScroll();
106
229
  });
107
230
 
231
+ watch(() => props.messages.length, async () => {
232
+ await refreshSpacerTracking();
233
+ });
234
+
108
235
  onMounted(async () => {
236
+ messageResizeObserver = new ResizeObserver(() => {
237
+ updateSpacerHeight();
238
+ });
239
+
109
240
  await import('@incremark/theme/styles.css')
110
241
  await agentStore.fetchPlaceholderMessages()
242
+ await refreshSpacerTracking();
111
243
  });
112
244
 
113
245
  onUnmounted(() => {
246
+ if (innerScrollContainerRef.value) {
247
+ innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
248
+ }
249
+
250
+ stopObservingLastMessages();
251
+ messageResizeObserver?.disconnect();
114
252
  agentStore.stopPlaceholderAnimation();
115
253
  });
116
254
 
117
- watch(scrollContainer, () => {
255
+ watch(scrollContainer, async () => {
256
+ if (innerScrollContainerRef.value) {
257
+ innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
258
+ }
259
+
118
260
  if (scrollContainer.value) {
119
261
  innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
120
262
 
121
- innerScrollContainerRef.value.addEventListener('scroll', () => {
122
- recalculateScroll();
123
- });
263
+ innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
264
+ await refreshSpacerTracking();
124
265
  }
125
266
  })
126
267
 
@@ -17,7 +17,7 @@
17
17
  :incremark-options="incremarkOptions"
18
18
  />
19
19
  <p v-else class="text-red-500 py-2">
20
- Error occured
20
+ Error occurred
21
21
  </p>
22
22
  </div>
23
23
  </template>
package/custom/types.ts CHANGED
@@ -25,7 +25,6 @@ export interface IToolGroup {
25
25
  }
26
26
 
27
27
  export interface IMessage {
28
- id: string;
29
28
  role: 'user' | 'assistant';
30
29
  metadata?: any,
31
30
  parts: IPart[];
@@ -19,6 +19,7 @@ export const contextSchema = z.object({
19
19
  userTimeZone: z.string(),
20
20
  sessionId: z.string(),
21
21
  turnId: z.string(),
22
+ httpExtra: z.custom().optional(),
22
23
  emitToolCallEvent: z.custom(),
23
24
  });
24
25
  function isLangChainAgentCompletionAdapter(adapter) {
@@ -130,7 +131,7 @@ export function createAgentChatModel(params) {
130
131
  }
131
132
  export function callAgent(params) {
132
133
  return __awaiter(this, void 0, void 0, function* () {
133
- const { name, model, summaryModel, modelMiddleware = [], checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
134
+ const { name, model, summaryModel, modelMiddleware = [], checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, httpExtra, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
134
135
  const tools = yield createAgentTools(customComponentsDir, apiBasedTools);
135
136
  const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
136
137
  const sequenceDebugMiddleware = createSequenceDebugMiddleware(sequenceDebugSink);
@@ -164,6 +165,7 @@ export function callAgent(params) {
164
165
  userTimeZone,
165
166
  sessionId,
166
167
  turnId,
168
+ httpExtra,
167
169
  emitToolCallEvent,
168
170
  },
169
171
  });
@@ -49,6 +49,7 @@ export function createApiTool(toolName, apiBasedTool) {
49
49
  const normalizedInput = (input !== null && input !== void 0 ? input : {});
50
50
  return apiBasedTool.call({
51
51
  adminUser: runtime.context.adminUser,
52
+ httpExtra: runtime.context.httpExtra,
52
53
  inputs: normalizedInput,
53
54
  userTimeZone: runtime.context.userTimeZone,
54
55
  });
@@ -7,11 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { AdminForthDataTypes, } from 'adminforth';
10
+ import { AdminForthDataTypes, logger, } from 'adminforth';
11
11
  import dayjs from 'dayjs';
12
12
  import timezone from 'dayjs/plugin/timezone.js';
13
13
  import utc from 'dayjs/plugin/utc.js';
14
- import { PassThrough } from 'stream';
15
14
  import { inspect } from 'util';
16
15
  import YAML from 'yaml';
17
16
  dayjs.extend(utc);
@@ -230,7 +229,7 @@ function formatDateTimeColumns(rows, dateTimeColumnNames, userTimeZone) {
230
229
  function applyToolOverride(params) {
231
230
  return __awaiter(this, void 0, void 0, function* () {
232
231
  var _a;
233
- const { adminforth, adminUser, capturedEndpointsByToolName, httpExtra, inputs, output, toolName, userTimeZone, } = params;
232
+ const { adminforth, adminUser, httpExtra, inputs, invokeTool, output, toolName, userTimeZone, } = params;
234
233
  const sanitizedOutput = sanitizeForYaml(output);
235
234
  const override = TOOL_OVERRIDES[toolName];
236
235
  if (!override) {
@@ -250,17 +249,10 @@ function applyToolOverride(params) {
250
249
  userTimeZone,
251
250
  invokeTool: (nestedToolName_1, ...args_1) => __awaiter(this, [nestedToolName_1, ...args_1], void 0, function* (nestedToolName, nestedParams = {}) {
252
251
  var _a, _b, _c;
253
- const nestedEndpoint = capturedEndpointsByToolName[nestedToolName];
254
- if (!nestedEndpoint) {
255
- throw new Error(`Tool ${nestedToolName} is not registered`);
256
- }
257
252
  const nestedInputs = (_a = nestedParams.inputs) !== null && _a !== void 0 ? _a : inputs;
258
253
  const nestedHttpExtra = (_b = nestedParams.httpExtra) !== null && _b !== void 0 ? _b : httpExtra;
259
254
  const nestedUserTimeZone = (_c = nestedParams.userTimeZone) !== null && _c !== void 0 ? _c : userTimeZone;
260
- const nestedOutput = yield callCapturedEndpoint({
261
- adminforth,
262
- endpoint: nestedEndpoint,
263
- adminUser,
255
+ const nestedOutput = yield invokeTool(nestedToolName, {
264
256
  inputs: nestedInputs,
265
257
  httpExtra: nestedHttpExtra,
266
258
  userTimeZone: nestedUserTimeZone,
@@ -268,9 +260,9 @@ function applyToolOverride(params) {
268
260
  return applyToolOverride({
269
261
  adminforth,
270
262
  adminUser,
271
- capturedEndpointsByToolName,
272
263
  httpExtra: nestedHttpExtra,
273
264
  inputs: nestedInputs,
265
+ invokeTool,
274
266
  output: nestedOutput,
275
267
  toolName: nestedToolName,
276
268
  userTimeZone: nestedUserTimeZone,
@@ -301,6 +293,9 @@ function stripAdminApiPrefix(path, adminforth) {
301
293
  function openApiSchemaPathToToolName(path, adminforth) {
302
294
  return endpointPathToToolName(stripAdminApiPrefix(path, adminforth));
303
295
  }
296
+ function formatLogNameList(names) {
297
+ return names.length ? names.join(', ') : '(none)';
298
+ }
304
299
  export function formatApiBasedToolCall(params) {
305
300
  return __awaiter(this, void 0, void 0, function* () {
306
301
  var _a;
@@ -326,69 +321,6 @@ function normalizeCookies(cookies) {
326
321
  }
327
322
  return Object.entries(cookies).map(([key, value]) => ({ key, value }));
328
323
  }
329
- function createToolResponse(baseResponse) {
330
- return {
331
- headers: [],
332
- status: 200,
333
- message: undefined,
334
- setHeader(name, value) {
335
- this.headers.push([name, value]);
336
- baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.setHeader(name, value);
337
- },
338
- setStatus(code, message) {
339
- this.status = code;
340
- this.message = message;
341
- baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.setStatus(code, message);
342
- },
343
- blobStream() {
344
- var _a;
345
- return (_a = baseResponse === null || baseResponse === void 0 ? void 0 : baseResponse.blobStream()) !== null && _a !== void 0 ? _a : new PassThrough();
346
- },
347
- };
348
- }
349
- function createRawExpressRequest(params) {
350
- const cookieHeader = params.cookies
351
- .map(({ key, value }) => `${key}=${value}`)
352
- .join('; ');
353
- return {
354
- adminUser: params.adminUser,
355
- body: params.body,
356
- destroyed: false,
357
- headers: Object.assign(Object.assign({}, params.headers), (cookieHeader ? { cookie: cookieHeader } : {})),
358
- method: params.method.toUpperCase(),
359
- on: () => undefined,
360
- query: params.query,
361
- url: params.requestUrl,
362
- };
363
- }
364
- function createRawExpressResponse(response) {
365
- const rawResponse = {
366
- destroyed: false,
367
- on: () => undefined,
368
- setHeader(name, value) {
369
- response.setHeader(name, value);
370
- return rawResponse;
371
- },
372
- status(code) {
373
- response.status = code;
374
- return rawResponse;
375
- },
376
- send(message) {
377
- response.message = message;
378
- return rawResponse;
379
- },
380
- json(payload) {
381
- response.jsonPayload = payload;
382
- response.message = JSON.stringify(payload);
383
- return rawResponse;
384
- },
385
- write: () => true,
386
- writeHead: () => rawResponse,
387
- writableEnded: false,
388
- end: () => rawResponse,
389
- };
390
- return rawResponse;
391
- }
392
324
  function normalizeDateTimeInputsToUtc(body, adminforth, userTimeZone) {
393
325
  if (!userTimeZone || typeof body.resourceId !== 'string') {
394
326
  return body;
@@ -437,90 +369,168 @@ function normalizeDateTimeInputsToUtc(body, adminforth, userTimeZone) {
437
369
  };
438
370
  return normalizeValue(body);
439
371
  }
440
- function callCapturedEndpoint(params) {
441
- return __awaiter(this, void 0, void 0, function* () {
442
- var _a, _b, _c, _d;
443
- const { adminforth, adminUser, endpoint, httpExtra, inputs, userTimeZone } = params;
444
- const response = createToolResponse(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.response);
445
- const headers = Object.assign({ 'content-type': 'application/json', 'X-TimeZone': userTimeZone }, ((_a = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers) !== null && _a !== void 0 ? _a : {}));
446
- const body = normalizeDateTimeInputsToUtc(((_b = inputs !== null && inputs !== void 0 ? inputs : httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.body) !== null && _b !== void 0 ? _b : {}), adminforth, headers['X-TimeZone']);
447
- const query = (_c = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.query) !== null && _c !== void 0 ? _c : {};
448
- const cookies = normalizeCookies(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.cookies);
449
- const requestUrl = (_d = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.requestUrl) !== null && _d !== void 0 ? _d : `${adminforth.config.baseUrl}/adminapi/v1${endpoint.path}`;
450
- const abortController = new AbortController();
451
- const rawRequest = createRawExpressRequest({
452
- adminUser,
453
- body,
454
- cookies,
455
- headers,
456
- method: endpoint.method,
457
- query,
458
- requestUrl,
459
- });
460
- const rawResponse = createRawExpressResponse(response);
461
- const acceptLanguage = headers['accept-language'];
462
- const tr = (msg, category = 'default', translationParams, pluralizationNumber) => adminforth.tr(msg, category, acceptLanguage, translationParams, pluralizationNumber);
463
- const output = yield endpoint.handler({
464
- body,
465
- adminUser,
466
- query,
467
- headers,
468
- cookies,
469
- response,
470
- requestUrl,
471
- abortSignal: abortController.signal,
472
- _raw_express_req: rawRequest,
473
- _raw_express_res: rawResponse,
474
- tr,
475
- });
476
- if (output !== undefined && output !== null) {
477
- return output;
372
+ const METHODS_WITHOUT_REQUEST_BODY = new Set(['GET', 'HEAD']);
373
+ const HEADERS_NOT_FORWARDED_TO_API_TOOL = new Set([
374
+ 'connection',
375
+ 'content-length',
376
+ 'host',
377
+ 'keep-alive',
378
+ 'proxy-authenticate',
379
+ 'proxy-authorization',
380
+ 'te',
381
+ 'trailer',
382
+ 'transfer-encoding',
383
+ 'upgrade',
384
+ ]);
385
+ function getHeaderValue(headers, headerName) {
386
+ var _a;
387
+ const normalizedHeaderName = headerName.toLowerCase();
388
+ const value = (_a = Object.entries(headers !== null && headers !== void 0 ? headers : {}).find(([name]) => name.toLowerCase() === normalizedHeaderName)) === null || _a === void 0 ? void 0 : _a[1];
389
+ if (typeof value !== 'string') {
390
+ return undefined;
391
+ }
392
+ return value.split(',')[0].trim();
393
+ }
394
+ function isAbsoluteHttpUrl(value) {
395
+ try {
396
+ const url = new URL(value);
397
+ return url.protocol === 'http:' || url.protocol === 'https:';
398
+ }
399
+ catch (_a) {
400
+ return false;
401
+ }
402
+ }
403
+ function getRequestOrigin(httpExtra) {
404
+ var _a, _b;
405
+ const requestUrl = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.requestUrl;
406
+ if (requestUrl && isAbsoluteHttpUrl(requestUrl)) {
407
+ return new URL(requestUrl).origin;
408
+ }
409
+ const host = (_a = getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'x-forwarded-host')) !== null && _a !== void 0 ? _a : getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'host');
410
+ if (!host) {
411
+ return undefined;
412
+ }
413
+ const protocol = (_b = getHeaderValue(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers, 'x-forwarded-proto')) !== null && _b !== void 0 ? _b : 'http';
414
+ return `${protocol}://${host}`;
415
+ }
416
+ function resolveOpenApiRequestUrl(params) {
417
+ if (isAbsoluteHttpUrl(params.path)) {
418
+ return params.path;
419
+ }
420
+ const origin = getRequestOrigin(params.httpExtra);
421
+ if (!origin) {
422
+ throw new Error(`Tool "${params.toolName}" has relative OpenAPI path "${params.path}" but request host header is unavailable.`);
423
+ }
424
+ return new URL(params.path, origin).toString();
425
+ }
426
+ function createToolRequestHeaders(httpExtra, userTimeZone) {
427
+ var _a;
428
+ const headers = {};
429
+ for (const [name, value] of Object.entries((_a = httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.headers) !== null && _a !== void 0 ? _a : {})) {
430
+ const headerName = name.toLowerCase();
431
+ if (typeof value === 'string' && !HEADERS_NOT_FORWARDED_TO_API_TOOL.has(headerName)) {
432
+ headers[headerName] = value;
478
433
  }
479
- if (response.jsonPayload !== undefined) {
480
- return response.jsonPayload;
434
+ }
435
+ headers.accept = 'application/json';
436
+ headers['content-type'] = 'application/json';
437
+ if (userTimeZone) {
438
+ headers['x-timezone'] = userTimeZone;
439
+ }
440
+ const cookieHeader = normalizeCookies(httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.cookies)
441
+ .map(({ key, value }) => `${key}=${value}`)
442
+ .join('; ');
443
+ if (cookieHeader && !headers.cookie) {
444
+ headers.cookie = cookieHeader;
445
+ }
446
+ return headers;
447
+ }
448
+ function appendInputsToQueryString(url, inputs) {
449
+ const nextUrl = new URL(url);
450
+ for (const [key, value] of Object.entries(inputs)) {
451
+ if (value === undefined) {
452
+ continue;
481
453
  }
482
- if (response.message !== undefined) {
483
- return response.message;
454
+ if (Array.isArray(value)) {
455
+ for (const item of value) {
456
+ nextUrl.searchParams.append(key, typeof item === 'object' && item !== null ? JSON.stringify(item) : String(item));
457
+ }
458
+ continue;
459
+ }
460
+ nextUrl.searchParams.set(key, typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value));
461
+ }
462
+ return nextUrl.toString();
463
+ }
464
+ function parseOpenApiToolResponse(response) {
465
+ return __awaiter(this, void 0, void 0, function* () {
466
+ var _a;
467
+ const responseText = yield response.text();
468
+ const payload = responseText && ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json'))
469
+ ? JSON.parse(responseText)
470
+ : responseText;
471
+ if (response.ok) {
472
+ return responseText ? payload : { status: response.status };
484
473
  }
485
474
  return {
486
- headers: response.headers,
475
+ error: 'HTTP_ERROR',
487
476
  status: response.status,
477
+ statusText: response.statusText,
478
+ response: payload,
488
479
  };
489
480
  });
490
481
  }
482
+ function callOpenApiSchema(params) {
483
+ return __awaiter(this, void 0, void 0, function* () {
484
+ var _a;
485
+ const { adminforth, httpExtra, inputs, schema, toolName, userTimeZone } = params;
486
+ const method = schema.method.toUpperCase();
487
+ const body = normalizeDateTimeInputsToUtc(((_a = inputs !== null && inputs !== void 0 ? inputs : httpExtra === null || httpExtra === void 0 ? void 0 : httpExtra.body) !== null && _a !== void 0 ? _a : {}), adminforth, userTimeZone);
488
+ const requestUrl = resolveOpenApiRequestUrl({
489
+ httpExtra,
490
+ path: schema.path,
491
+ toolName,
492
+ });
493
+ const hasRequestBody = !METHODS_WITHOUT_REQUEST_BODY.has(method);
494
+ const response = yield fetch(hasRequestBody ? requestUrl : appendInputsToQueryString(requestUrl, body), {
495
+ method,
496
+ headers: createToolRequestHeaders(httpExtra, userTimeZone),
497
+ body: hasRequestBody ? JSON.stringify(body) : undefined,
498
+ });
499
+ return parseOpenApiToolResponse(response);
500
+ });
501
+ }
491
502
  export function prepareApiBasedTools(adminforth) {
492
- const capturedEndpoints = [];
493
- const captureServer = {
494
- setupSpaServer() { },
495
- endpoint: ((options) => {
496
- var _a, _b;
497
- capturedEndpoints.push(Object.assign(Object.assign({}, options), { response_schema: (_a = options.response_schema) !== null && _a !== void 0 ? _a : options.responce_schema, normalizedResponseSchema: (_b = options.response_schema) !== null && _b !== void 0 ? _b : options.responce_schema }));
498
- }),
499
- };
500
- adminforth.setupEndpoints(captureServer);
501
503
  const apiBasedTools = {};
502
- const capturedEndpointsByToolName = Object.fromEntries(capturedEndpoints.map((endpoint) => [endpointPathToToolName(endpoint.path), endpoint]));
503
504
  const openApiSchemas = adminforth.openApi.registeredSchemas.filter((schema) => schema.request_schema || schema.response_schema);
504
505
  const openApiSchemasByToolName = new Map();
505
506
  for (const schema of openApiSchemas) {
506
507
  const toolName = openApiSchemaPathToToolName(schema.path, adminforth);
507
508
  openApiSchemasByToolName.set(toolName, schema);
508
509
  }
510
+ logger.info(`AdminForth Agent OpenAPI APIs: ${formatLogNameList(adminforth.openApi.registeredSchemas.map((schema) => openApiSchemaPathToToolName(schema.path, adminforth)))}`);
511
+ logger.info(`AdminForth Agent OpenAPI tools connected: ${formatLogNameList([...openApiSchemasByToolName.keys()])}`);
509
512
  for (const [toolName, schema] of openApiSchemasByToolName.entries()) {
510
- const endpoint = capturedEndpointsByToolName[toolName];
511
513
  apiBasedTools[toolName] = {
512
514
  description: schema.description,
513
515
  input_schema: schema.request_schema,
514
516
  input_schma: schema.request_schema,
515
517
  output_schema: schema.response_schema,
516
518
  call: (...args_1) => __awaiter(this, [...args_1], void 0, function* ({ adminUser, adminuser, inputs, httpExtra, userTimeZone } = {}) {
517
- if (!endpoint) {
518
- throw new Error(`Tool "${toolName}" is defined in OpenAPI but has no callable AdminForth endpoint handler.`);
519
- }
520
- const output = yield callCapturedEndpoint({
521
- adminforth,
522
- endpoint,
523
- adminUser: adminUser !== null && adminUser !== void 0 ? adminUser : adminuser,
519
+ const invokeTool = (nextToolName_1, ...args_2) => __awaiter(this, [nextToolName_1, ...args_2], void 0, function* (nextToolName, nextParams = {}) {
520
+ const nextSchema = openApiSchemasByToolName.get(nextToolName);
521
+ if (!nextSchema) {
522
+ throw new Error(`Tool ${nextToolName} is not registered in OpenAPI`);
523
+ }
524
+ return callOpenApiSchema({
525
+ adminforth,
526
+ schema: nextSchema,
527
+ toolName: nextToolName,
528
+ inputs: nextParams.inputs,
529
+ httpExtra: nextParams.httpExtra,
530
+ userTimeZone: nextParams.userTimeZone,
531
+ });
532
+ });
533
+ const output = yield invokeTool(toolName, {
524
534
  inputs,
525
535
  httpExtra,
526
536
  userTimeZone,
@@ -528,9 +538,9 @@ export function prepareApiBasedTools(adminforth) {
528
538
  const processedOutput = yield applyToolOverride({
529
539
  adminforth,
530
540
  adminUser: adminUser !== null && adminUser !== void 0 ? adminUser : adminuser,
531
- capturedEndpointsByToolName,
532
541
  httpExtra,
533
542
  inputs,
543
+ invokeTool,
534
544
  output,
535
545
  toolName,
536
546
  userTimeZone,