@adminforth/agent 1.23.0 → 1.24.1
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/README.md +31 -0
- package/agent/middleware/apiBasedTools.ts +27 -4
- package/agent/middleware/sequenceDebug.ts +39 -2
- package/agent/simpleAgent.ts +283 -52
- package/agent/toolCallEvents.ts +3 -0
- package/apiBasedTools.ts +98 -8
- package/build.log +2 -2
- package/custom/composables/useAgentStore.ts +3 -0
- package/custom/package.json +1 -1
- package/dist/agent/middleware/apiBasedTools.js +22 -3
- package/dist/agent/middleware/sequenceDebug.js +18 -2
- package/dist/agent/simpleAgent.js +167 -27
- package/dist/agent/toolCallEvents.js +1 -0
- package/dist/apiBasedTools.js +73 -6
- package/dist/custom/composables/useAgentStore.ts +3 -0
- package/dist/custom/package.json +1 -1
- package/dist/index.js +32 -18
- package/index.ts +33 -15
- package/package.json +16 -5
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# AdminForth Agent Plugin
|
|
2
|
+
|
|
3
|
+
<img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /> <img src="https://woodpecker.devforth.io/api/badges/3848/status.svg" alt="Build Status" /> <a href="https://www.npmjs.com/package/@adminforth/agent"><img src="https://img.shields.io/npm/dm/@adminforth/agent" alt="npm downloads" /></a> <a href="https://www.npmjs.com/package/@adminforth/agent"><img src="https://img.shields.io/npm/v/@adminforth/agent" alt="npm version" /></a>
|
|
4
|
+
|
|
5
|
+
[](https://tluma.ai/ask-ai/devforth/adminforth)
|
|
6
|
+
|
|
7
|
+
Adds a native AI agent to your AdminForth application.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Connect AdminForth with native AI agent workflows.
|
|
12
|
+
- Expose API-based tools to agent-driven tasks.
|
|
13
|
+
- Extend back-office operations with custom agent actions.
|
|
14
|
+
- Keep agent capabilities close to your admin logic.
|
|
15
|
+
|
|
16
|
+
## Documentation
|
|
17
|
+
|
|
18
|
+
Full setup and configuration guide:
|
|
19
|
+
|
|
20
|
+
[AdminForth Agent Documentation](https://adminforth.dev/docs/tutorial/Plugins/agent/)
|
|
21
|
+
|
|
22
|
+
## About AdminForth
|
|
23
|
+
|
|
24
|
+
AdminForth is an open-source, agent-first admin framework for building robust admin panels and back-office applications faster.
|
|
25
|
+
|
|
26
|
+
## Related links
|
|
27
|
+
|
|
28
|
+
- [AdminForth website](https://adminforth.dev)
|
|
29
|
+
- [npm package](https://www.npmjs.com/package/@adminforth/agent)
|
|
30
|
+
- [More AdminForth plugins](https://adminforth.dev/docs/tutorial/ListOfPlugins/)
|
|
31
|
+
- [Built by DevForth](https://devforth.io)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { ToolMessage } from "@langchain/core/messages";
|
|
2
2
|
import { createMiddleware } from "langchain";
|
|
3
|
-
import { logger } from "adminforth";
|
|
4
|
-
import {
|
|
3
|
+
import { logger, type AdminUser, type IAdminForth } from "adminforth";
|
|
4
|
+
import {
|
|
5
|
+
formatApiBasedToolCall,
|
|
6
|
+
type ApiBasedTool,
|
|
7
|
+
} from "../../apiBasedTools.js";
|
|
5
8
|
import {
|
|
6
9
|
createToolCallTracker,
|
|
7
10
|
type ToolCallEventSink,
|
|
@@ -46,6 +49,7 @@ function getEnabledApiToolNames(messages: unknown[]) {
|
|
|
46
49
|
|
|
47
50
|
export function createApiBasedToolsMiddleware(
|
|
48
51
|
apiBasedTools: Record<string, ApiBasedTool>,
|
|
52
|
+
adminforth: IAdminForth,
|
|
49
53
|
) {
|
|
50
54
|
const alwaysAvailableApiToolNames = new Set<string>(ALWAYS_AVAILABLE_API_TOOL_NAMES);
|
|
51
55
|
const dynamicTools = Object.fromEntries(
|
|
@@ -71,14 +75,33 @@ export function createApiBasedToolsMiddleware(
|
|
|
71
75
|
async wrapToolCall(request, handler) {
|
|
72
76
|
const startedAt = Date.now();
|
|
73
77
|
const toolInput = JSON.stringify(request.toolCall.args ?? {});
|
|
74
|
-
const { emitToolCallEvent } = request.runtime.context as {
|
|
78
|
+
const { adminUser, emitToolCallEvent, userTimeZone } = request.runtime.context as {
|
|
79
|
+
adminUser: AdminUser;
|
|
75
80
|
emitToolCallEvent: ToolCallEventSink;
|
|
81
|
+
userTimeZone: string;
|
|
76
82
|
};
|
|
83
|
+
const toolArgs = (request.toolCall.args ?? {}) as Record<string, unknown>;
|
|
84
|
+
let toolInfo: string | undefined;
|
|
85
|
+
|
|
86
|
+
if (request.toolCall.name === "fetch_skill") {
|
|
87
|
+
toolInfo = `Load ${(toolArgs.skillName as string).split("_").join(" ")} skill`;
|
|
88
|
+
} else if (request.toolCall.name === "fetch_tool_schema") {
|
|
89
|
+
toolInfo = `Load ${(toolArgs.toolName as string).split("_").join(" ")} tool `;
|
|
90
|
+
} else {
|
|
91
|
+
toolInfo = await formatApiBasedToolCall({
|
|
92
|
+
adminforth,
|
|
93
|
+
adminUser,
|
|
94
|
+
inputs: toolArgs,
|
|
95
|
+
toolName: request.toolCall.name,
|
|
96
|
+
userTimeZone,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
77
99
|
const toolCallTracker = createToolCallTracker({
|
|
78
100
|
emit: emitToolCallEvent,
|
|
79
101
|
toolCallId: request.toolCall.id,
|
|
80
102
|
toolName: request.toolCall.name,
|
|
81
|
-
|
|
103
|
+
toolInfo,
|
|
104
|
+
input: toolArgs,
|
|
82
105
|
startedAt,
|
|
83
106
|
});
|
|
84
107
|
toolCallTracker.start();
|
|
@@ -122,11 +122,32 @@ function finalizeSequenceDebug(sequence: PendingSequenceDebug): SequenceDebug {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
type OpenAiResponsesDebugModel = {
|
|
125
|
-
|
|
125
|
+
getName?: () => string;
|
|
126
|
+
model?: string;
|
|
126
127
|
zdrEnabled?: boolean;
|
|
127
|
-
|
|
128
|
+
_defaultConfig?: {
|
|
129
|
+
modelProvider?: string;
|
|
130
|
+
};
|
|
131
|
+
invocationParams?: (options?: Record<string, unknown>) => Record<string, unknown>;
|
|
128
132
|
};
|
|
129
133
|
|
|
134
|
+
function getDebugModelName(model: OpenAiResponsesDebugModel) {
|
|
135
|
+
return typeof model.getName === "function" ? model.getName() : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function supportsOpenAiResponseDebug(
|
|
139
|
+
model: OpenAiResponsesDebugModel,
|
|
140
|
+
): model is OpenAiResponsesDebugModel & {
|
|
141
|
+
model: string;
|
|
142
|
+
invocationParams: (options?: Record<string, unknown>) => Record<string, unknown>;
|
|
143
|
+
} {
|
|
144
|
+
return (
|
|
145
|
+
getDebugModelName(model) === "ChatOpenAI" &&
|
|
146
|
+
typeof model.model === "string" &&
|
|
147
|
+
typeof model.invocationParams === "function"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
130
151
|
function stringifyPromptForDebug(params: {
|
|
131
152
|
model: OpenAiResponsesDebugModel;
|
|
132
153
|
systemMessage: { text: string };
|
|
@@ -137,6 +158,22 @@ function stringifyPromptForDebug(params: {
|
|
|
137
158
|
}) {
|
|
138
159
|
const { model, systemMessage, messages, tools, toolChoice, modelSettings } = params;
|
|
139
160
|
|
|
161
|
+
if (!supportsOpenAiResponseDebug(model)) {
|
|
162
|
+
return YAML.stringify({
|
|
163
|
+
model: {
|
|
164
|
+
name: getDebugModelName(model) ?? null,
|
|
165
|
+
provider: model._defaultConfig?.modelProvider ?? null,
|
|
166
|
+
configuredModel:
|
|
167
|
+
typeof model.model === "string" ? model.model : null,
|
|
168
|
+
},
|
|
169
|
+
systemMessage,
|
|
170
|
+
messages,
|
|
171
|
+
...(tools.length > 0 ? { tools } : {}),
|
|
172
|
+
...(toolChoice !== undefined ? { toolChoice } : {}),
|
|
173
|
+
...(modelSettings ? { modelSettings } : {}),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
140
177
|
return YAML.stringify({
|
|
141
178
|
input: convertMessagesToResponsesInput({
|
|
142
179
|
messages: [
|
package/agent/simpleAgent.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
1
2
|
import { createAgent, summarizationMiddleware } from "langchain";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
MODEL_PROVIDER_CONFIG,
|
|
5
|
+
getChatModelByClassName,
|
|
6
|
+
} from "langchain/chat_models/universal";
|
|
7
|
+
import {
|
|
8
|
+
logger,
|
|
9
|
+
type AdminUser,
|
|
10
|
+
type CompletionAdapter,
|
|
11
|
+
type IAdminForth,
|
|
12
|
+
} from "adminforth";
|
|
3
13
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
4
14
|
import {type BaseCheckpointSaver, type Messages } from "@langchain/langgraph";
|
|
5
15
|
import type { LLMResult } from "@langchain/core/outputs";
|
|
6
16
|
import { z } from "zod";
|
|
7
|
-
import { ChatOpenAI } from "@langchain/openai";
|
|
8
17
|
import { createAgentTools } from "./tools/index.js";
|
|
9
18
|
import { createApiBasedToolsMiddleware } from "./middleware/apiBasedTools.js";
|
|
10
19
|
import {
|
|
@@ -23,9 +32,24 @@ export const contextSchema = z.object({
|
|
|
23
32
|
emitToolCallEvent: z.custom<ToolCallEventSink>(),
|
|
24
33
|
});
|
|
25
34
|
|
|
26
|
-
type
|
|
35
|
+
export type AgentModelProvider = "openai" | "anthropic" | "google-genai";
|
|
36
|
+
export type AgentChatModel = BaseChatModel<any, any>;
|
|
37
|
+
|
|
38
|
+
type ProviderBackedCompletionAdapter = CompletionAdapter & {
|
|
39
|
+
constructor?: {
|
|
40
|
+
name?: string;
|
|
41
|
+
};
|
|
27
42
|
options?: {
|
|
28
43
|
openAiApiKey?: string;
|
|
44
|
+
openAIApiKey?: string;
|
|
45
|
+
anthropicApiKey?: string;
|
|
46
|
+
geminiApiKey?: string;
|
|
47
|
+
googleApiKey?: string;
|
|
48
|
+
googleGenAiApiKey?: string;
|
|
49
|
+
googleGenerativeAiApiKey?: string;
|
|
50
|
+
apiKey?: string;
|
|
51
|
+
provider?: string;
|
|
52
|
+
modelProvider?: string;
|
|
29
53
|
model?: string;
|
|
30
54
|
baseURL?: string;
|
|
31
55
|
baseUrl?: string;
|
|
@@ -34,7 +58,9 @@ type OpenAIBackedCompletionAdapter = CompletionAdapter & {
|
|
|
34
58
|
};
|
|
35
59
|
};
|
|
36
60
|
|
|
37
|
-
type
|
|
61
|
+
type AgentChatModelConstructor = new (
|
|
62
|
+
fields?: Record<string, unknown>,
|
|
63
|
+
) => AgentChatModel;
|
|
38
64
|
|
|
39
65
|
type LlmOutputTokenUsage = {
|
|
40
66
|
promptTokens?: unknown;
|
|
@@ -51,6 +77,233 @@ type PendingLlmRun = {
|
|
|
51
77
|
firstTokenAt?: number;
|
|
52
78
|
};
|
|
53
79
|
|
|
80
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
81
|
+
return typeof value === "object" && value !== null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeProvider(value: unknown): AgentModelProvider | undefined {
|
|
85
|
+
if (typeof value !== "string") {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const normalized = value.toLowerCase().replace(/[_\s]+/g, "-");
|
|
90
|
+
|
|
91
|
+
if (["openai", "open-ai"].includes(normalized)) {
|
|
92
|
+
return "openai";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (["anthropic", "claude"].includes(normalized)) {
|
|
96
|
+
return "anthropic";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
[
|
|
101
|
+
"google",
|
|
102
|
+
"gemini",
|
|
103
|
+
"google-genai",
|
|
104
|
+
"google-gemini",
|
|
105
|
+
"google-generative-ai",
|
|
106
|
+
"google-generativeai",
|
|
107
|
+
].includes(normalized)
|
|
108
|
+
) {
|
|
109
|
+
return "google-genai";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function detectProviderFromConstructorName(
|
|
116
|
+
constructorName: string | undefined,
|
|
117
|
+
): AgentModelProvider | undefined {
|
|
118
|
+
const normalized = constructorName?.toLowerCase();
|
|
119
|
+
|
|
120
|
+
if (!normalized) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (normalized.includes("openai")) {
|
|
125
|
+
return "openai";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (normalized.includes("anthropic") || normalized.includes("claude")) {
|
|
129
|
+
return "anthropic";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (normalized.includes("gemini") || normalized.includes("google")) {
|
|
133
|
+
return "google-genai";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function detectProviderFromModelName(
|
|
140
|
+
model: string | undefined,
|
|
141
|
+
): AgentModelProvider | undefined {
|
|
142
|
+
const normalized = model?.toLowerCase();
|
|
143
|
+
|
|
144
|
+
if (!normalized) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (normalized.startsWith("claude")) {
|
|
149
|
+
return "anthropic";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (normalized.startsWith("gemini")) {
|
|
153
|
+
return "google-genai";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (/^(gpt|o[1-9]|chatgpt)/.test(normalized)) {
|
|
157
|
+
return "openai";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function detectAgentModelProvider(
|
|
164
|
+
adapter: ProviderBackedCompletionAdapter,
|
|
165
|
+
): AgentModelProvider {
|
|
166
|
+
const options = adapter.options ?? {};
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
normalizeProvider(options.modelProvider) ??
|
|
170
|
+
normalizeProvider(options.provider) ??
|
|
171
|
+
detectProviderFromConstructorName(adapter.constructor?.name) ??
|
|
172
|
+
(options.openAiApiKey || options.openAIApiKey
|
|
173
|
+
? "openai"
|
|
174
|
+
: undefined) ??
|
|
175
|
+
(options.anthropicApiKey ? "anthropic" : undefined) ??
|
|
176
|
+
(options.geminiApiKey ||
|
|
177
|
+
options.googleApiKey ||
|
|
178
|
+
options.googleGenAiApiKey ||
|
|
179
|
+
options.googleGenerativeAiApiKey
|
|
180
|
+
? "google-genai"
|
|
181
|
+
: undefined) ??
|
|
182
|
+
detectProviderFromModelName(options.model) ??
|
|
183
|
+
(() => {
|
|
184
|
+
throw new Error(
|
|
185
|
+
"Could not infer completion adapter provider. Set options.modelProvider to openai, anthropic, or google-genai.",
|
|
186
|
+
);
|
|
187
|
+
})()
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getProviderApiKey(
|
|
192
|
+
provider: AgentModelProvider,
|
|
193
|
+
options: ProviderBackedCompletionAdapter["options"],
|
|
194
|
+
) {
|
|
195
|
+
switch (provider) {
|
|
196
|
+
case "openai":
|
|
197
|
+
return options?.openAiApiKey ?? options?.openAIApiKey ?? options?.apiKey;
|
|
198
|
+
case "anthropic":
|
|
199
|
+
return options?.anthropicApiKey ?? options?.apiKey;
|
|
200
|
+
case "google-genai":
|
|
201
|
+
return (
|
|
202
|
+
options?.geminiApiKey ??
|
|
203
|
+
options?.googleApiKey ??
|
|
204
|
+
options?.googleGenAiApiKey ??
|
|
205
|
+
options?.googleGenerativeAiApiKey ??
|
|
206
|
+
options?.apiKey
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getProviderModel(
|
|
212
|
+
provider: AgentModelProvider,
|
|
213
|
+
options: ProviderBackedCompletionAdapter["options"],
|
|
214
|
+
) {
|
|
215
|
+
if (options?.model) {
|
|
216
|
+
return options.model;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (provider === "openai") {
|
|
220
|
+
return "gpt-5-nano";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (provider === "google-genai") {
|
|
224
|
+
return "gemini-3-flash-preview";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw new Error(
|
|
228
|
+
`CompletionAdapter for provider ${provider} must expose options.model`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function buildChatModelConfig(params: {
|
|
233
|
+
provider: AgentModelProvider;
|
|
234
|
+
options: ProviderBackedCompletionAdapter["options"];
|
|
235
|
+
maxTokens: number;
|
|
236
|
+
}) {
|
|
237
|
+
const { provider, options, maxTokens } = params;
|
|
238
|
+
const apiKey = getProviderApiKey(provider, options);
|
|
239
|
+
|
|
240
|
+
if (!apiKey) {
|
|
241
|
+
const optionName =
|
|
242
|
+
provider === "openai"
|
|
243
|
+
? "options.openAiApiKey"
|
|
244
|
+
: provider === "anthropic"
|
|
245
|
+
? "options.anthropicApiKey"
|
|
246
|
+
: "options.geminiApiKey";
|
|
247
|
+
|
|
248
|
+
throw new Error(
|
|
249
|
+
`CompletionAdapter must expose ${optionName} for ${provider} agent mode`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const model = getProviderModel(provider, options);
|
|
254
|
+
const baseURL = options?.baseURL ?? options?.baseUrl;
|
|
255
|
+
const extraRequestBodyParameters = {
|
|
256
|
+
...(options?.extraRequestBodyParameters ?? {}),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
if (provider === "openai" && isRecord(extraRequestBodyParameters.reasoning)) {
|
|
260
|
+
extraRequestBodyParameters.reasoning = {
|
|
261
|
+
...extraRequestBodyParameters.reasoning,
|
|
262
|
+
summary: "auto",
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const config: Record<string, unknown> = {
|
|
267
|
+
model,
|
|
268
|
+
apiKey,
|
|
269
|
+
maxTokens,
|
|
270
|
+
streaming: true,
|
|
271
|
+
...extraRequestBodyParameters,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
if (typeof options?.timeoutMs === "number") {
|
|
275
|
+
config.timeout = options.timeoutMs;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (baseURL) {
|
|
279
|
+
config.baseURL = baseURL;
|
|
280
|
+
config.baseUrl = baseURL;
|
|
281
|
+
config.configuration = {
|
|
282
|
+
baseURL,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (provider === "openai") {
|
|
287
|
+
config.openAIApiKey = apiKey;
|
|
288
|
+
config.useResponsesApi = true;
|
|
289
|
+
config.outputVersion = "v1";
|
|
290
|
+
config.promptCacheKey = `adminforth-agent:${model}:system-v1:tools-v1`;
|
|
291
|
+
config.promptCacheRetention = "in_memory";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (provider === "anthropic") {
|
|
295
|
+
config.anthropicApiKey = apiKey;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (provider === "google-genai") {
|
|
299
|
+
config.geminiApiKey = apiKey;
|
|
300
|
+
config.googleApiKey = apiKey;
|
|
301
|
+
config.maxOutputTokens = maxTokens;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return { model, config };
|
|
305
|
+
}
|
|
306
|
+
|
|
54
307
|
function getFiniteNumber(value: unknown) {
|
|
55
308
|
return typeof value === "number" && Number.isFinite(value)
|
|
56
309
|
? value
|
|
@@ -162,63 +415,39 @@ function createAgentLlmMetricsLogger() {
|
|
|
162
415
|
return new AgentLlmMetricsLogger();
|
|
163
416
|
}
|
|
164
417
|
|
|
165
|
-
export function createAgentChatModel(params: {
|
|
418
|
+
export async function createAgentChatModel(params: {
|
|
166
419
|
adapter: CompletionAdapter;
|
|
167
420
|
maxTokens: number;
|
|
168
421
|
}) {
|
|
169
|
-
const adapter = params.adapter as
|
|
422
|
+
const adapter = params.adapter as ProviderBackedCompletionAdapter;
|
|
170
423
|
const options = adapter.options ?? {};
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const model = options.model ?? "gpt-5-nano";
|
|
179
|
-
const baseURL = options.baseURL ?? options.baseUrl;
|
|
180
|
-
const reasoning = options.extraRequestBodyParameters
|
|
181
|
-
?.reasoning as OpenAiReasoningConfig | undefined;
|
|
182
|
-
const reasoningConfig = reasoning
|
|
183
|
-
? {
|
|
184
|
-
...reasoning,
|
|
185
|
-
summary: "auto",
|
|
186
|
-
}
|
|
187
|
-
: undefined;
|
|
188
|
-
|
|
189
|
-
// @ts-ignore
|
|
190
|
-
return new ChatOpenAI({
|
|
191
|
-
apiKey: options.openAiApiKey,
|
|
192
|
-
model,
|
|
424
|
+
const provider = detectAgentModelProvider(adapter);
|
|
425
|
+
const { config } = buildChatModelConfig({
|
|
426
|
+
provider,
|
|
427
|
+
options,
|
|
193
428
|
maxTokens: params.maxTokens,
|
|
194
|
-
useResponsesApi: true,
|
|
195
|
-
outputVersion: "v1",
|
|
196
|
-
streaming: true,
|
|
197
|
-
|
|
198
|
-
promptCacheKey: `adminforth-agent:${model}:system-v1:tools-v1`,
|
|
199
|
-
promptCacheRetention: "in_memory",
|
|
200
|
-
|
|
201
|
-
...(reasoningConfig ? { reasoning: reasoningConfig } : {}),
|
|
202
|
-
...(typeof options.timeoutMs === "number"
|
|
203
|
-
? { timeout: options.timeoutMs }
|
|
204
|
-
: {}),
|
|
205
|
-
...(baseURL
|
|
206
|
-
? {
|
|
207
|
-
configuration: {
|
|
208
|
-
baseURL,
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
: {}),
|
|
212
429
|
});
|
|
430
|
+
const className = MODEL_PROVIDER_CONFIG[provider].className;
|
|
431
|
+
const ChatModelClass = await getChatModelByClassName(
|
|
432
|
+
className,
|
|
433
|
+
provider,
|
|
434
|
+
) as AgentChatModelConstructor;
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
model: new ChatModelClass(config),
|
|
438
|
+
provider,
|
|
439
|
+
};
|
|
213
440
|
}
|
|
214
441
|
|
|
215
442
|
export async function callAgent(params: {
|
|
216
443
|
name: string;
|
|
217
|
-
model:
|
|
218
|
-
summaryModel:
|
|
444
|
+
model: AgentChatModel;
|
|
445
|
+
summaryModel: AgentChatModel;
|
|
446
|
+
modelProvider: AgentModelProvider;
|
|
219
447
|
checkpointer?: BaseCheckpointSaver;
|
|
220
448
|
messages: Messages;
|
|
221
449
|
adminUser: AdminUser;
|
|
450
|
+
adminforth: IAdminForth;
|
|
222
451
|
apiBasedTools: Record<string, ApiBasedTool>;
|
|
223
452
|
customComponentsDir: string;
|
|
224
453
|
sessionId: string;
|
|
@@ -231,9 +460,11 @@ export async function callAgent(params: {
|
|
|
231
460
|
name,
|
|
232
461
|
model,
|
|
233
462
|
summaryModel,
|
|
463
|
+
modelProvider,
|
|
234
464
|
checkpointer,
|
|
235
465
|
messages,
|
|
236
466
|
adminUser,
|
|
467
|
+
adminforth,
|
|
237
468
|
apiBasedTools,
|
|
238
469
|
customComponentsDir,
|
|
239
470
|
sessionId,
|
|
@@ -244,16 +475,16 @@ export async function callAgent(params: {
|
|
|
244
475
|
} = params;
|
|
245
476
|
|
|
246
477
|
const tools = await createAgentTools(customComponentsDir, apiBasedTools);
|
|
247
|
-
const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools);
|
|
248
|
-
const openAiResponsesContinuationMiddleware =
|
|
249
|
-
createOpenAiResponsesContinuationMiddleware();
|
|
478
|
+
const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
|
|
250
479
|
const sequenceDebugMiddleware = createSequenceDebugMiddleware(
|
|
251
480
|
sequenceDebugSink,
|
|
252
481
|
);
|
|
253
482
|
|
|
254
483
|
const middleware = [
|
|
255
484
|
apiBasedToolsMiddleware,
|
|
256
|
-
|
|
485
|
+
...(modelProvider === "openai"
|
|
486
|
+
? [createOpenAiResponsesContinuationMiddleware()]
|
|
487
|
+
: []),
|
|
257
488
|
sequenceDebugMiddleware,
|
|
258
489
|
summarizationMiddleware({
|
|
259
490
|
model: summaryModel,
|
package/agent/toolCallEvents.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type ToolCallEvent =
|
|
|
6
6
|
| {
|
|
7
7
|
toolCallId: string;
|
|
8
8
|
toolName: string;
|
|
9
|
+
toolInfo?: string;
|
|
9
10
|
phase: "start";
|
|
10
11
|
input: string;
|
|
11
12
|
}
|
|
@@ -64,6 +65,7 @@ export function createToolCallTracker(params: {
|
|
|
64
65
|
emit: ToolCallEventSink;
|
|
65
66
|
toolCallId?: string;
|
|
66
67
|
toolName: string;
|
|
68
|
+
toolInfo?: string;
|
|
67
69
|
input?: Record<string, unknown>;
|
|
68
70
|
startedAt?: number;
|
|
69
71
|
}) {
|
|
@@ -75,6 +77,7 @@ export function createToolCallTracker(params: {
|
|
|
75
77
|
params.emit({
|
|
76
78
|
toolCallId,
|
|
77
79
|
toolName: params.toolName,
|
|
80
|
+
toolInfo: params.toolInfo,
|
|
78
81
|
phase: "start",
|
|
79
82
|
input: YAML.stringify(params.input ?? {}),
|
|
80
83
|
});
|