@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
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
>
|
|
35
35
|
|
|
36
36
|
<div
|
|
37
|
-
v-for="(message, index) in props.messages" :key="
|
|
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
|
|
68
|
-
import { useTemplateRef, ref,
|
|
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
|
|
89
|
-
|
|
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
|
|
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
|
-
|
|
123
|
-
});
|
|
263
|
+
innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
|
|
264
|
+
await refreshSpacerTracking();
|
|
124
265
|
}
|
|
125
266
|
})
|
|
126
267
|
|
package/custom/types.ts
CHANGED
|
@@ -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
|
});
|
package/dist/apiBasedTools.js
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
480
|
-
|
|
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 (
|
|
483
|
-
|
|
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
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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,
|