@adminforth/agent 1.22.2 → 1.24.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/apiBasedTools.ts CHANGED
@@ -65,10 +65,11 @@ type ToolHttpResponse = IAdminForthHttpResponse & {
65
65
  type ToolOverrideCallParams = Pick<ApiBasedToolCallParams, 'httpExtra' | 'inputs' | 'userTimeZone'>;
66
66
 
67
67
  type ToolOverrideContext = {
68
- output: unknown;
68
+ output?: unknown;
69
69
  adminUser?: AdminUser;
70
70
  httpExtra?: Partial<HttpExtra>;
71
71
  inputs?: Record<string, unknown>;
72
+ resourceLabel?: string;
72
73
  userTimeZone?: string;
73
74
  invokeTool: (toolName: string, params?: ToolOverrideCallParams) => Promise<unknown>;
74
75
  };
@@ -94,8 +95,46 @@ type GetResourceDataToolResponse = {
94
95
  options?: Record<string, unknown>;
95
96
  };
96
97
 
98
+ type DateTimeColumnType = AdminForthDataTypes.DATETIME | AdminForthDataTypes.TIME;
99
+
97
100
  const DEFAULT_USER_TIME_ZONE = 'UTC';
98
101
 
102
+ function getInputString(inputs: Record<string, unknown> | undefined, key: string) {
103
+ const value = inputs?.[key];
104
+
105
+ return typeof value === 'string' && value ? value : undefined;
106
+ }
107
+
108
+ function getInputArrayLength(inputs: Record<string, unknown> | undefined, key: string) {
109
+ const value = inputs?.[key];
110
+
111
+ return Array.isArray(value) ? value.length : undefined;
112
+ }
113
+
114
+ function resourceLabel(adminforth: IAdminForth, inputs: Record<string, unknown> | undefined) {
115
+ const resourceId = getInputString(inputs, 'resourceId');
116
+ const resource = adminforth.config.resources.find((res) => res.resourceId === resourceId);
117
+
118
+ return resource?.label ?? resourceId ?? 'resource';
119
+ }
120
+
121
+ function getDataPrefix(inputs: Record<string, unknown> | undefined) {
122
+ const offset = typeof inputs?.offset === 'number' ? inputs.offset : undefined;
123
+ const limit = typeof inputs?.limit === 'number' ? inputs.limit : undefined;
124
+
125
+ if (offset !== undefined && limit !== undefined) {
126
+ return `${offset}-${offset + limit} `;
127
+ }
128
+
129
+ return limit === undefined ? '' : `${limit} `;
130
+ }
131
+
132
+ function actionText(inputs: Record<string, unknown> | undefined) {
133
+ const actionId = getInputString(inputs, 'actionId');
134
+
135
+ return actionId ? ` action ${actionId}` : ' action';
136
+ }
137
+
99
138
  const TOOL_OVERRIDES: Record<string, ToolOverride> = {
100
139
  get_resource: {
101
140
  wipe_frontend_specific_data: [
@@ -104,11 +143,12 @@ const TOOL_OVERRIDES: Record<string, ToolOverride> = {
104
143
  'resource.options.actions[].customComponent',
105
144
  'resource.options.pageInjections',
106
145
  ],
107
- format_tool: async ({ }) => {
108
- return "get resource Apartments"
109
- }
146
+ format_tool: ({ resourceLabel }) => `Get ${resourceLabel} resource`,
110
147
  },
111
148
  get_resource_data: {
149
+ format_tool: ({ inputs, resourceLabel }) => (
150
+ `Get ${getDataPrefix(inputs)}${resourceLabel}`
151
+ ),
112
152
  post_process_response: async ({ output, inputs, invokeTool, userTimeZone }) => {
113
153
  if (hasToolError(output)) {
114
154
  return output;
@@ -130,9 +170,37 @@ const TOOL_OVERRIDES: Record<string, ToolOverride> = {
130
170
 
131
171
  return response;
132
172
  },
133
- format_tool: async ({ }) => {
134
- return "get 1-20 Apartment filtered listed=yes"
135
- }
173
+ },
174
+ aggregate: {
175
+ format_tool: ({ resourceLabel }) => `Aggregate ${resourceLabel}`,
176
+ },
177
+ start_custom_action: {
178
+ format_tool: ({ inputs, resourceLabel }) => `Run ${resourceLabel}${actionText(inputs)}`,
179
+ },
180
+ start_custom_bulk_action: {
181
+ format_tool: ({ inputs, resourceLabel }) => {
182
+ const recordCount = getInputArrayLength(inputs, 'recordIds');
183
+ const recordsText = recordCount === undefined ? '' : ` for ${recordCount} records`;
184
+
185
+ return `Run ${resourceLabel}${actionText(inputs)}${recordsText}`;
186
+ },
187
+ },
188
+ start_bulk_action: {
189
+ format_tool: ({ inputs, resourceLabel }) => {
190
+ const recordCount = getInputArrayLength(inputs, 'recordIds');
191
+ const recordsText = recordCount === undefined ? '' : ` for ${recordCount} records`;
192
+
193
+ return `Run ${resourceLabel}${actionText(inputs)}${recordsText}`;
194
+ },
195
+ },
196
+ create_record: {
197
+ format_tool: ({ resourceLabel }) => `Create ${resourceLabel}`,
198
+ },
199
+ update_record: {
200
+ format_tool: ({ resourceLabel }) => `Update ${resourceLabel}`,
201
+ },
202
+ delete_record: {
203
+ format_tool: ({ resourceLabel }) => `Delete ${resourceLabel}`,
136
204
  },
137
205
  };
138
206
 
@@ -373,6 +441,7 @@ async function applyToolOverride(params: {
373
441
  adminUser,
374
442
  inputs: nestedInputs,
375
443
  httpExtra: nestedHttpExtra,
444
+ userTimeZone: nestedUserTimeZone,
376
445
  });
377
446
 
378
447
  return applyToolOverride({
@@ -398,7 +467,31 @@ function endpointPathToToolName(path: string) {
398
467
  .replace(/^_+|_+$/g, '');
399
468
  }
400
469
 
401
- function normalizeCookies(cookies?: Partial<HttpExtra>['cookies']): CookieItem[] {
470
+ export async function formatApiBasedToolCall(params: {
471
+ adminforth: IAdminForth;
472
+ adminUser?: AdminUser;
473
+ httpExtra?: Partial<HttpExtra>;
474
+ inputs?: Record<string, unknown>;
475
+ toolName: string;
476
+ userTimeZone?: string;
477
+ }) {
478
+ const formatTool = TOOL_OVERRIDES[params.toolName]?.format_tool;
479
+
480
+ return await formatTool?.({
481
+ adminUser: params.adminUser,
482
+ httpExtra: params.httpExtra,
483
+ inputs: params.inputs,
484
+ resourceLabel: resourceLabel(params.adminforth, params.inputs),
485
+ userTimeZone: params.userTimeZone,
486
+ invokeTool: async () => {
487
+ throw new Error('Tool info formatting cannot invoke tools');
488
+ },
489
+ });
490
+ }
491
+
492
+ function normalizeCookies(
493
+ cookies?: Partial<HttpExtra>['cookies'] | Record<string, string>,
494
+ ): CookieItem[] {
402
495
  if (!cookies) {
403
496
  return [];
404
497
  }
@@ -488,20 +581,104 @@ function createRawExpressResponse(response: ToolHttpResponse) {
488
581
  return rawResponse;
489
582
  }
490
583
 
584
+ function normalizeDateTimeInputsToUtc(
585
+ body: Record<string, unknown>,
586
+ adminforth: IAdminForth,
587
+ userTimeZone?: string,
588
+ ): Record<string, unknown> {
589
+ if (!userTimeZone || typeof body.resourceId !== 'string') {
590
+ return body;
591
+ }
592
+
593
+ const resource = adminforth.config.resources.find((res) => res.resourceId === body.resourceId);
594
+
595
+ if (!resource) {
596
+ return body;
597
+ }
598
+
599
+ const columnsByName = new Map(resource.dataSourceColumns.map((column) => [column.name, column]));
600
+
601
+ const normalizeColumnValue = (
602
+ value: unknown,
603
+ columnType: DateTimeColumnType,
604
+ ): unknown => {
605
+ if (Array.isArray(value)) {
606
+ return value.map((item) => normalizeColumnValue(item, columnType));
607
+ }
608
+
609
+ if (typeof value !== 'string' || value === '') {
610
+ return value;
611
+ }
612
+
613
+ if (columnType === AdminForthDataTypes.DATETIME) {
614
+ return dayjs.tz(value, userTimeZone).utc().toISOString();
615
+ }
616
+
617
+ if (columnType === AdminForthDataTypes.TIME) {
618
+ const userDate = dayjs().tz(userTimeZone).format('YYYY-MM-DD');
619
+ return dayjs.tz(`${userDate}T${value}`, userTimeZone).utc().format('HH:mm:ss');
620
+ }
621
+ };
622
+
623
+ const normalizeValue = (value: unknown, key?: string): unknown => {
624
+ const column = key ? columnsByName.get(key) : undefined;
625
+
626
+ if (column?.type === AdminForthDataTypes.DATETIME || column?.type === AdminForthDataTypes.TIME) {
627
+ return normalizeColumnValue(value, column.type);
628
+ }
629
+
630
+ if (Array.isArray(value)) {
631
+ return value.map((item) => normalizeValue(item));
632
+ }
633
+
634
+ if (!value || typeof value !== 'object') {
635
+ return value;
636
+ }
637
+
638
+ const record = value as Record<string, unknown>;
639
+ const filterColumn = typeof record.field === 'string' ? columnsByName.get(record.field) : undefined;
640
+
641
+ if (
642
+ 'value' in record &&
643
+ (filterColumn?.type === AdminForthDataTypes.DATETIME || filterColumn?.type === AdminForthDataTypes.TIME)
644
+ ) {
645
+ return {
646
+ ...record,
647
+ value: normalizeColumnValue(record.value, filterColumn.type),
648
+ };
649
+ }
650
+
651
+ return Object.fromEntries(
652
+ Object.entries(record).map(([nestedKey, nestedValue]) => [
653
+ nestedKey,
654
+ normalizeValue(nestedValue, nestedKey),
655
+ ]),
656
+ );
657
+ };
658
+
659
+ return normalizeValue(body) as Record<string, unknown>;
660
+ }
661
+
491
662
  async function callCapturedEndpoint(params: {
492
663
  adminforth: IAdminForth;
493
664
  adminUser?: AdminUser;
494
665
  endpoint: CapturedEndpoint;
495
666
  httpExtra?: Partial<HttpExtra>;
496
667
  inputs?: Record<string, unknown>;
668
+ userTimeZone?: string;
497
669
  }) {
498
- const { adminforth, adminUser, endpoint, httpExtra, inputs } = params;
670
+ const { adminforth, adminUser, endpoint, httpExtra, inputs, userTimeZone } = params;
499
671
  const response = createToolResponse(httpExtra?.response);
500
672
  const headers = {
501
673
  'content-type': 'application/json',
674
+ 'X-TimeZone': userTimeZone,
502
675
  ...(httpExtra?.headers ?? {}),
503
676
  };
504
- const body = (inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>;
677
+ const body = normalizeDateTimeInputsToUtc(
678
+ (inputs ?? httpExtra?.body ?? {}) as Record<string, unknown>,
679
+ adminforth,
680
+ headers['X-TimeZone'],
681
+ );
505
682
  const query = httpExtra?.query ?? {};
506
683
  const cookies = normalizeCookies(httpExtra?.cookies);
507
684
  const requestUrl = httpExtra?.requestUrl ?? `${adminforth.config.baseUrl}/adminapi/v1${endpoint.path}`;
@@ -596,6 +773,7 @@ export function prepareApiBasedTools(adminforth: IAdminForth): Record<string, Ap
596
773
  adminUser: adminUser ?? adminuser,
597
774
  inputs,
598
775
  httpExtra,
776
+ userTimeZone,
599
777
  });
600
778
 
601
779
  const processedOutput = await applyToolOverride({
@@ -629,4 +807,4 @@ export function serializeApiBasedTool(tool: ApiBasedTool | undefined) {
629
807
  output_schema: tool.output_schema,
630
808
  call: '[Function]',
631
809
  };
632
- }
810
+ }
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 199,703 bytes received 562 bytes 400,530.00 bytes/sec
42
- total size is 197,399 speedup is 0.99
41
+ sent 200,003 bytes received 562 bytes 401,130.00 bytes/sec
42
+ total size is 197,699 speedup is 0.99
@@ -7,7 +7,7 @@
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
8
8
  },
9
9
  "keywords": [],
10
- "author": "",
10
+ "author": "DevForth (https://devforth.io)",
11
11
  "license": "ISC",
12
12
  "packageManager": "pnpm@10.33.0",
13
13
  "dependencies": {
@@ -73,8 +73,7 @@ Are you sure?
73
73
  ## Updating
74
74
 
75
75
  You can use tool `update_record` tool it updates fields of record. To update `allowedActions.edit` should be set to true and
76
- `updated` column `showIn.edit` should be true at the same time. If one of this condition is not met, explain to user that is
77
- not allowed to edit
76
+ `updated` column `showIn.edit` should be true at the same time. If one of this condition is not met, explain to user that is not allowed to edit
78
77
 
79
78
  In addition to instructions above show user the table of edits (old value/new value)
80
79
 
@@ -126,6 +125,10 @@ After creation of new record also show user a link to this record. If several re
126
125
 
127
126
  Omit any pictures or file paths, you are not capable of doing it. If they are not required all is good, if they are required, explain to user that you are not able to create record because of this reason.
128
127
 
128
+ ### Working with dates
129
+
130
+ When you create or update date or datetime fields, please use ISO format for this. For example, "2024-01-01" for date and "2024-01-01T12:00:00Z" for datetime. If user provides date in different format, try to parse it and convert to ISO format.
131
+
129
132
  ### Example
130
133
 
131
134
 
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { ToolMessage } from "@langchain/core/messages";
11
11
  import { createMiddleware } from "langchain";
12
12
  import { logger } from "adminforth";
13
+ import { formatApiBasedToolCall, } from "../../apiBasedTools.js";
13
14
  import { createToolCallTracker, } from "../toolCallEvents.js";
14
15
  import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "../tools/index.js";
15
16
  import { createApiTool } from "../tools/apiTool.js";
@@ -40,7 +41,7 @@ function getEnabledApiToolNames(messages) {
40
41
  }
41
42
  return enabledToolNames;
42
43
  }
43
- export function createApiBasedToolsMiddleware(apiBasedTools) {
44
+ export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
44
45
  const alwaysAvailableApiToolNames = new Set(ALWAYS_AVAILABLE_API_TOOL_NAMES);
45
46
  const dynamicTools = Object.fromEntries(Object.entries(apiBasedTools).map(([toolName, apiBasedTool]) => [
46
47
  toolName,
@@ -62,12 +63,30 @@ export function createApiBasedToolsMiddleware(apiBasedTools) {
62
63
  var _a, _b, _c, _d;
63
64
  const startedAt = Date.now();
64
65
  const toolInput = JSON.stringify((_a = request.toolCall.args) !== null && _a !== void 0 ? _a : {});
65
- const { emitToolCallEvent } = request.runtime.context;
66
+ const { adminUser, emitToolCallEvent, userTimeZone } = request.runtime.context;
67
+ const toolArgs = ((_b = request.toolCall.args) !== null && _b !== void 0 ? _b : {});
68
+ let toolInfo;
69
+ if (request.toolCall.name === "fetch_skill") {
70
+ toolInfo = `Load ${toolArgs.skillName.split("_").join(" ")} skill`;
71
+ }
72
+ else if (request.toolCall.name === "fetch_tool_schema") {
73
+ toolInfo = `Load ${toolArgs.toolName.split("_").join(" ")} tool `;
74
+ }
75
+ else {
76
+ toolInfo = yield formatApiBasedToolCall({
77
+ adminforth,
78
+ adminUser,
79
+ inputs: toolArgs,
80
+ toolName: request.toolCall.name,
81
+ userTimeZone,
82
+ });
83
+ }
66
84
  const toolCallTracker = createToolCallTracker({
67
85
  emit: emitToolCallEvent,
68
86
  toolCallId: request.toolCall.id,
69
87
  toolName: request.toolCall.name,
70
- input: ((_b = request.toolCall.args) !== null && _b !== void 0 ? _b : {}),
88
+ toolInfo,
89
+ input: toolArgs,
71
90
  startedAt,
72
91
  });
73
92
  toolCallTracker.start();
@@ -66,15 +66,31 @@ function finalizeSequenceDebug(sequence) {
66
66
  resultType: (_a = sequence.resultType) !== null && _a !== void 0 ? _a : "final_text",
67
67
  };
68
68
  }
69
+ function getDebugModelName(model) {
70
+ return typeof model.getName === "function" ? model.getName() : undefined;
71
+ }
72
+ function supportsOpenAiResponseDebug(model) {
73
+ return (getDebugModelName(model) === "ChatOpenAI" &&
74
+ typeof model.model === "string" &&
75
+ typeof model.invocationParams === "function");
76
+ }
69
77
  function stringifyPromptForDebug(params) {
70
- var _a;
78
+ var _a, _b, _c, _d;
71
79
  const { model, systemMessage, messages, tools, toolChoice, modelSettings } = params;
80
+ if (!supportsOpenAiResponseDebug(model)) {
81
+ return YAML.stringify(Object.assign(Object.assign(Object.assign({ model: {
82
+ name: (_a = getDebugModelName(model)) !== null && _a !== void 0 ? _a : null,
83
+ provider: (_c = (_b = model._defaultConfig) === null || _b === void 0 ? void 0 : _b.modelProvider) !== null && _c !== void 0 ? _c : null,
84
+ configuredModel: typeof model.model === "string" ? model.model : null,
85
+ }, systemMessage,
86
+ messages }, (tools.length > 0 ? { tools } : {})), (toolChoice !== undefined ? { toolChoice } : {})), (modelSettings ? { modelSettings } : {})));
87
+ }
72
88
  return YAML.stringify(Object.assign({ input: convertMessagesToResponsesInput({
73
89
  messages: [
74
90
  ...(systemMessage.text === "" ? [] : [systemMessage]),
75
91
  ...messages,
76
92
  ],
77
- zdrEnabled: (_a = model.zdrEnabled) !== null && _a !== void 0 ? _a : false,
93
+ zdrEnabled: (_d = model.zdrEnabled) !== null && _d !== void 0 ? _d : false,
78
94
  model: model.model,
79
95
  }) }, model.invocationParams(Object.assign(Object.assign(Object.assign({}, (modelSettings !== null && modelSettings !== void 0 ? modelSettings : {})), (tools.length > 0 ? { tools } : {})), (toolChoice !== undefined ? { tool_choice: toolChoice } : {})))));
80
96
  }
@@ -8,10 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { createAgent, summarizationMiddleware } from "langchain";
11
- import { logger } from "adminforth";
11
+ import { MODEL_PROVIDER_CONFIG, getChatModelByClassName, } from "langchain/chat_models/universal";
12
+ import { logger, } from "adminforth";
12
13
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
13
14
  import { z } from "zod";
14
- import { ChatOpenAI } from "@langchain/openai";
15
15
  import { createAgentTools } from "./tools/index.js";
16
16
  import { createApiBasedToolsMiddleware } from "./middleware/apiBasedTools.js";
17
17
  import { createSequenceDebugMiddleware, } from "./middleware/sequenceDebug.js";
@@ -23,6 +23,149 @@ export const contextSchema = z.object({
23
23
  turnId: z.string(),
24
24
  emitToolCallEvent: z.custom(),
25
25
  });
26
+ function isRecord(value) {
27
+ return typeof value === "object" && value !== null;
28
+ }
29
+ function normalizeProvider(value) {
30
+ if (typeof value !== "string") {
31
+ return undefined;
32
+ }
33
+ const normalized = value.toLowerCase().replace(/[_\s]+/g, "-");
34
+ if (["openai", "open-ai"].includes(normalized)) {
35
+ return "openai";
36
+ }
37
+ if (["anthropic", "claude"].includes(normalized)) {
38
+ return "anthropic";
39
+ }
40
+ if ([
41
+ "google",
42
+ "gemini",
43
+ "google-genai",
44
+ "google-gemini",
45
+ "google-generative-ai",
46
+ "google-generativeai",
47
+ ].includes(normalized)) {
48
+ return "google-genai";
49
+ }
50
+ return undefined;
51
+ }
52
+ function detectProviderFromConstructorName(constructorName) {
53
+ const normalized = constructorName === null || constructorName === void 0 ? void 0 : constructorName.toLowerCase();
54
+ if (!normalized) {
55
+ return undefined;
56
+ }
57
+ if (normalized.includes("openai")) {
58
+ return "openai";
59
+ }
60
+ if (normalized.includes("anthropic") || normalized.includes("claude")) {
61
+ return "anthropic";
62
+ }
63
+ if (normalized.includes("gemini") || normalized.includes("google")) {
64
+ return "google-genai";
65
+ }
66
+ return undefined;
67
+ }
68
+ function detectProviderFromModelName(model) {
69
+ const normalized = model === null || model === void 0 ? void 0 : model.toLowerCase();
70
+ if (!normalized) {
71
+ return undefined;
72
+ }
73
+ if (normalized.startsWith("claude")) {
74
+ return "anthropic";
75
+ }
76
+ if (normalized.startsWith("gemini")) {
77
+ return "google-genai";
78
+ }
79
+ if (/^(gpt|o[1-9]|chatgpt)/.test(normalized)) {
80
+ return "openai";
81
+ }
82
+ return undefined;
83
+ }
84
+ function detectAgentModelProvider(adapter) {
85
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
86
+ const options = (_a = adapter.options) !== null && _a !== void 0 ? _a : {};
87
+ return ((_j = (_h = (_g = (_f = (_e = (_c = (_b = normalizeProvider(options.modelProvider)) !== null && _b !== void 0 ? _b : normalizeProvider(options.provider)) !== null && _c !== void 0 ? _c : detectProviderFromConstructorName((_d = adapter.constructor) === null || _d === void 0 ? void 0 : _d.name)) !== null && _e !== void 0 ? _e : (options.openAiApiKey || options.openAIApiKey
88
+ ? "openai"
89
+ : undefined)) !== null && _f !== void 0 ? _f : (options.anthropicApiKey ? "anthropic" : undefined)) !== null && _g !== void 0 ? _g : (options.geminiApiKey ||
90
+ options.googleApiKey ||
91
+ options.googleGenAiApiKey ||
92
+ options.googleGenerativeAiApiKey
93
+ ? "google-genai"
94
+ : undefined)) !== null && _h !== void 0 ? _h : detectProviderFromModelName(options.model)) !== null && _j !== void 0 ? _j : (() => {
95
+ throw new Error("Could not infer completion adapter provider. Set options.modelProvider to openai, anthropic, or google-genai.");
96
+ })());
97
+ }
98
+ function getProviderApiKey(provider, options) {
99
+ var _a, _b, _c, _d, _e, _f, _g;
100
+ switch (provider) {
101
+ case "openai":
102
+ return (_b = (_a = options === null || options === void 0 ? void 0 : options.openAiApiKey) !== null && _a !== void 0 ? _a : options === null || options === void 0 ? void 0 : options.openAIApiKey) !== null && _b !== void 0 ? _b : options === null || options === void 0 ? void 0 : options.apiKey;
103
+ case "anthropic":
104
+ return (_c = options === null || options === void 0 ? void 0 : options.anthropicApiKey) !== null && _c !== void 0 ? _c : options === null || options === void 0 ? void 0 : options.apiKey;
105
+ case "google-genai":
106
+ return ((_g = (_f = (_e = (_d = options === null || options === void 0 ? void 0 : options.geminiApiKey) !== null && _d !== void 0 ? _d : options === null || options === void 0 ? void 0 : options.googleApiKey) !== null && _e !== void 0 ? _e : options === null || options === void 0 ? void 0 : options.googleGenAiApiKey) !== null && _f !== void 0 ? _f : options === null || options === void 0 ? void 0 : options.googleGenerativeAiApiKey) !== null && _g !== void 0 ? _g : options === null || options === void 0 ? void 0 : options.apiKey);
107
+ }
108
+ }
109
+ function getProviderModel(provider, options) {
110
+ if (options === null || options === void 0 ? void 0 : options.model) {
111
+ return options.model;
112
+ }
113
+ if (provider === "openai") {
114
+ return "gpt-5-nano";
115
+ }
116
+ if (provider === "google-genai") {
117
+ return "gemini-3-flash-preview";
118
+ }
119
+ throw new Error(`CompletionAdapter for provider ${provider} must expose options.model`);
120
+ }
121
+ function buildChatModelConfig(params) {
122
+ var _a, _b;
123
+ const { provider, options, maxTokens } = params;
124
+ const apiKey = getProviderApiKey(provider, options);
125
+ if (!apiKey) {
126
+ const optionName = provider === "openai"
127
+ ? "options.openAiApiKey"
128
+ : provider === "anthropic"
129
+ ? "options.anthropicApiKey"
130
+ : "options.geminiApiKey";
131
+ throw new Error(`CompletionAdapter must expose ${optionName} for ${provider} agent mode`);
132
+ }
133
+ const model = getProviderModel(provider, options);
134
+ const baseURL = (_a = options === null || options === void 0 ? void 0 : options.baseURL) !== null && _a !== void 0 ? _a : options === null || options === void 0 ? void 0 : options.baseUrl;
135
+ const extraRequestBodyParameters = Object.assign({}, ((_b = options === null || options === void 0 ? void 0 : options.extraRequestBodyParameters) !== null && _b !== void 0 ? _b : {}));
136
+ if (provider === "openai" && isRecord(extraRequestBodyParameters.reasoning)) {
137
+ extraRequestBodyParameters.reasoning = Object.assign(Object.assign({}, extraRequestBodyParameters.reasoning), { summary: "auto" });
138
+ }
139
+ const config = Object.assign({ model,
140
+ apiKey,
141
+ maxTokens, streaming: true }, extraRequestBodyParameters);
142
+ if (typeof (options === null || options === void 0 ? void 0 : options.timeoutMs) === "number") {
143
+ config.timeout = options.timeoutMs;
144
+ }
145
+ if (baseURL) {
146
+ config.baseURL = baseURL;
147
+ config.baseUrl = baseURL;
148
+ config.configuration = {
149
+ baseURL,
150
+ };
151
+ }
152
+ if (provider === "openai") {
153
+ config.openAIApiKey = apiKey;
154
+ config.useResponsesApi = true;
155
+ config.outputVersion = "v1";
156
+ config.promptCacheKey = `adminforth-agent:${model}:system-v1:tools-v1`;
157
+ config.promptCacheRetention = "in_memory";
158
+ }
159
+ if (provider === "anthropic") {
160
+ config.anthropicApiKey = apiKey;
161
+ }
162
+ if (provider === "google-genai") {
163
+ config.geminiApiKey = apiKey;
164
+ config.googleApiKey = apiKey;
165
+ config.maxOutputTokens = maxTokens;
166
+ }
167
+ return { model, config };
168
+ }
26
169
  function getFiniteNumber(value) {
27
170
  return typeof value === "number" && Number.isFinite(value)
28
171
  ? value
@@ -102,38 +245,35 @@ function createAgentLlmMetricsLogger() {
102
245
  return new AgentLlmMetricsLogger();
103
246
  }
104
247
  export function createAgentChatModel(params) {
105
- var _a, _b, _c, _d;
106
- const adapter = params.adapter;
107
- const options = (_a = adapter.options) !== null && _a !== void 0 ? _a : {};
108
- if (!options.openAiApiKey) {
109
- throw new Error("CompletionAdapter must expose options.openAiApiKey for ChatOpenAI");
110
- }
111
- const model = (_b = options.model) !== null && _b !== void 0 ? _b : "gpt-5-nano";
112
- const baseURL = (_c = options.baseURL) !== null && _c !== void 0 ? _c : options.baseUrl;
113
- const reasoning = (_d = options.extraRequestBodyParameters) === null || _d === void 0 ? void 0 : _d.reasoning;
114
- const reasoningConfig = reasoning
115
- ? Object.assign(Object.assign({}, reasoning), { summary: "auto" }) : undefined;
116
- // @ts-ignore
117
- return new ChatOpenAI(Object.assign(Object.assign(Object.assign({ apiKey: options.openAiApiKey, model, maxTokens: params.maxTokens, useResponsesApi: true, outputVersion: "v1", streaming: true, promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`, promptCacheRetention: "in_memory" }, (reasoningConfig ? { reasoning: reasoningConfig } : {})), (typeof options.timeoutMs === "number"
118
- ? { timeout: options.timeoutMs }
119
- : {})), (baseURL
120
- ? {
121
- configuration: {
122
- baseURL,
123
- },
124
- }
125
- : {})));
248
+ return __awaiter(this, void 0, void 0, function* () {
249
+ var _a;
250
+ const adapter = params.adapter;
251
+ const options = (_a = adapter.options) !== null && _a !== void 0 ? _a : {};
252
+ const provider = detectAgentModelProvider(adapter);
253
+ const { config } = buildChatModelConfig({
254
+ provider,
255
+ options,
256
+ maxTokens: params.maxTokens,
257
+ });
258
+ const className = MODEL_PROVIDER_CONFIG[provider].className;
259
+ const ChatModelClass = yield getChatModelByClassName(className, provider);
260
+ return {
261
+ model: new ChatModelClass(config),
262
+ provider,
263
+ };
264
+ });
126
265
  }
127
266
  export function callAgent(params) {
128
267
  return __awaiter(this, void 0, void 0, function* () {
129
- const { name, model, summaryModel, checkpointer, messages, adminUser, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
268
+ const { name, model, summaryModel, modelProvider, checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
130
269
  const tools = yield createAgentTools(customComponentsDir, apiBasedTools);
131
- const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools);
132
- const openAiResponsesContinuationMiddleware = createOpenAiResponsesContinuationMiddleware();
270
+ const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
133
271
  const sequenceDebugMiddleware = createSequenceDebugMiddleware(sequenceDebugSink);
134
272
  const middleware = [
135
273
  apiBasedToolsMiddleware,
136
- openAiResponsesContinuationMiddleware,
274
+ ...(modelProvider === "openai"
275
+ ? [createOpenAiResponsesContinuationMiddleware()]
276
+ : []),
137
277
  sequenceDebugMiddleware,
138
278
  summarizationMiddleware({
139
279
  model: summaryModel,
@@ -39,6 +39,7 @@ export function createToolCallTracker(params) {
39
39
  params.emit({
40
40
  toolCallId,
41
41
  toolName: params.toolName,
42
+ toolInfo: params.toolInfo,
42
43
  phase: "start",
43
44
  input: YAML.stringify((_a = params.input) !== null && _a !== void 0 ? _a : {}),
44
45
  });