@adminforth/agent 1.24.1 → 1.24.3

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.
@@ -1,5 +1,4 @@
1
1
  import { AIMessage } from "@langchain/core/messages";
2
- import { convertMessagesToResponsesInput } from "@langchain/openai";
3
2
  import { createMiddleware } from "langchain";
4
3
  import YAML from "yaml";
5
4
  import type { ToolCallEvent } from "../toolCallEvents.js";
@@ -121,35 +120,21 @@ function finalizeSequenceDebug(sequence: PendingSequenceDebug): SequenceDebug {
121
120
  };
122
121
  }
123
122
 
124
- type OpenAiResponsesDebugModel = {
123
+ type SequenceDebugPromptModel = {
125
124
  getName?: () => string;
126
125
  model?: string;
127
- zdrEnabled?: boolean;
128
126
  _defaultConfig?: {
129
127
  modelProvider?: string;
130
128
  };
131
129
  invocationParams?: (options?: Record<string, unknown>) => Record<string, unknown>;
132
130
  };
133
131
 
134
- function getDebugModelName(model: OpenAiResponsesDebugModel) {
132
+ function getDebugModelName(model: SequenceDebugPromptModel) {
135
133
  return typeof model.getName === "function" ? model.getName() : undefined;
136
134
  }
137
135
 
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
-
151
136
  function stringifyPromptForDebug(params: {
152
- model: OpenAiResponsesDebugModel;
137
+ model: SequenceDebugPromptModel;
153
138
  systemMessage: { text: string };
154
139
  messages: unknown[];
155
140
  tools: unknown[];
@@ -157,37 +142,28 @@ function stringifyPromptForDebug(params: {
157
142
  modelSettings?: Record<string, unknown>;
158
143
  }) {
159
144
  const { model, systemMessage, messages, tools, toolChoice, modelSettings } = params;
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
- }
145
+ const invocationParams =
146
+ typeof model.invocationParams === "function"
147
+ ? model.invocationParams({
148
+ ...(modelSettings ?? {}),
149
+ ...(tools.length > 0 ? { tools } : {}),
150
+ ...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}),
151
+ })
152
+ : null;
176
153
 
177
154
  return YAML.stringify({
178
- input: convertMessagesToResponsesInput({
179
- messages: [
180
- ...(systemMessage.text === "" ? [] : [systemMessage]),
181
- ...messages,
182
- ] as any[],
183
- zdrEnabled: model.zdrEnabled ?? false,
184
- model: model.model,
185
- }),
186
- ...model.invocationParams({
187
- ...(modelSettings ?? {}),
155
+ model: {
156
+ name: getDebugModelName(model) ?? null,
157
+ provider: model._defaultConfig?.modelProvider ?? null,
158
+ configuredModel:
159
+ typeof model.model === "string" ? model.model : null,
160
+ },
161
+ systemMessage,
162
+ messages,
188
163
  ...(tools.length > 0 ? { tools } : {}),
189
- ...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}),
190
- }),
164
+ ...(toolChoice !== undefined ? { toolChoice } : {}),
165
+ ...(modelSettings ? { modelSettings } : {}),
166
+ ...(invocationParams ? { invocationParams } : {}),
191
167
  });
192
168
  }
193
169
 
@@ -386,7 +362,7 @@ export function createSequenceDebugMiddleware(
386
362
  name: "SequenceDebugMiddleware",
387
363
  async wrapModelCall(request, handler) {
388
364
  const prompt = stringifyPromptForDebug({
389
- model: request.model as unknown as OpenAiResponsesDebugModel,
365
+ model: request.model as unknown as SequenceDebugPromptModel,
390
366
  systemMessage: request.systemMessage,
391
367
  messages: request.messages,
392
368
  tools: request.tools,
@@ -1,9 +1,5 @@
1
1
  import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
2
2
  import { createAgent, summarizationMiddleware } from "langchain";
3
- import {
4
- MODEL_PROVIDER_CONFIG,
5
- getChatModelByClassName,
6
- } from "langchain/chat_models/universal";
7
3
  import {
8
4
  logger,
9
5
  type AdminUser,
@@ -20,7 +16,6 @@ import {
20
16
  createSequenceDebugMiddleware,
21
17
  type SequenceDebugModelCallSink,
22
18
  } from "./middleware/sequenceDebug.js";
23
- import { createOpenAiResponsesContinuationMiddleware } from "./middleware/openAiResponsesContinuation.js";
24
19
  import type { ApiBasedTool } from "../apiBasedTools.js";
25
20
  import type { ToolCallEventSink } from "./toolCallEvents.js";
26
21
 
@@ -32,35 +27,27 @@ export const contextSchema = z.object({
32
27
  emitToolCallEvent: z.custom<ToolCallEventSink>(),
33
28
  });
34
29
 
35
- export type AgentModelProvider = "openai" | "anthropic" | "google-genai";
36
30
  export type AgentChatModel = BaseChatModel<any, any>;
37
-
38
- type ProviderBackedCompletionAdapter = CompletionAdapter & {
39
- constructor?: {
40
- name?: string;
41
- };
42
- options?: {
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;
53
- model?: string;
54
- baseURL?: string;
55
- baseUrl?: string;
56
- timeoutMs?: number;
57
- extraRequestBodyParameters?: Record<string, unknown>;
31
+ export type AgentModelPurpose = "primary" | "summary";
32
+ export type AgentModeCompletionAdapter = CompletionAdapter & {
33
+ getLangChainAgentSpec(params: {
34
+ maxTokens: number;
35
+ purpose: AgentModelPurpose;
36
+ }): Promise<{
37
+ model: unknown;
38
+ middleware?: unknown[];
39
+ }> | {
40
+ model: unknown;
41
+ middleware?: unknown[];
58
42
  };
59
43
  };
60
44
 
61
- type AgentChatModelConstructor = new (
62
- fields?: Record<string, unknown>,
63
- ) => AgentChatModel;
45
+ type AgentMiddleware = ReturnType<typeof createSequenceDebugMiddleware>;
46
+
47
+ type AgentChatModelSpec = {
48
+ model: AgentChatModel;
49
+ middleware: AgentMiddleware[];
50
+ };
64
51
 
65
52
  type LlmOutputTokenUsage = {
66
53
  promptTokens?: unknown;
@@ -77,231 +64,27 @@ type PendingLlmRun = {
77
64
  firstTokenAt?: number;
78
65
  };
79
66
 
80
- function isRecord(value: unknown): value is Record<string, unknown> {
81
- return typeof value === "object" && value !== null;
67
+ function isLangChainAgentCompletionAdapter(
68
+ adapter: CompletionAdapter,
69
+ ): adapter is AgentModeCompletionAdapter {
70
+ return typeof (adapter as AgentModeCompletionAdapter)
71
+ .getLangChainAgentSpec === "function";
82
72
  }
83
73
 
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"];
74
+ async function getAgentChatModelSpec(params: {
75
+ adapter: AgentModeCompletionAdapter;
235
76
  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
- }
77
+ purpose: AgentModelPurpose;
78
+ }): Promise<AgentChatModelSpec> {
79
+ const spec = await params.adapter.getLangChainAgentSpec({
80
+ maxTokens: params.maxTokens,
81
+ purpose: params.purpose,
82
+ });
265
83
 
266
- const config: Record<string, unknown> = {
267
- model,
268
- apiKey,
269
- maxTokens,
270
- streaming: true,
271
- ...extraRequestBodyParameters,
84
+ return {
85
+ model: spec.model as AgentChatModel,
86
+ middleware: (spec.middleware ?? []) as AgentMiddleware[],
272
87
  };
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
88
  }
306
89
 
307
90
  function getFiniteNumber(value: unknown) {
@@ -418,32 +201,26 @@ function createAgentLlmMetricsLogger() {
418
201
  export async function createAgentChatModel(params: {
419
202
  adapter: CompletionAdapter;
420
203
  maxTokens: number;
204
+ purpose: AgentModelPurpose;
421
205
  }) {
422
- const adapter = params.adapter as ProviderBackedCompletionAdapter;
423
- const options = adapter.options ?? {};
424
- const provider = detectAgentModelProvider(adapter);
425
- const { config } = buildChatModelConfig({
426
- provider,
427
- options,
206
+ if (!isLangChainAgentCompletionAdapter(params.adapter)) {
207
+ throw new Error(
208
+ "AdminForth Agent requires completionAdapter to implement getLangChainAgentSpec({ maxTokens, purpose }).",
209
+ );
210
+ }
211
+
212
+ return await getAgentChatModelSpec({
213
+ adapter: params.adapter,
428
214
  maxTokens: params.maxTokens,
215
+ purpose: params.purpose,
429
216
  });
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
- };
440
217
  }
441
218
 
442
219
  export async function callAgent(params: {
443
220
  name: string;
444
221
  model: AgentChatModel;
445
222
  summaryModel: AgentChatModel;
446
- modelProvider: AgentModelProvider;
223
+ modelMiddleware?: AgentMiddleware[];
447
224
  checkpointer?: BaseCheckpointSaver;
448
225
  messages: Messages;
449
226
  adminUser: AdminUser;
@@ -460,7 +237,7 @@ export async function callAgent(params: {
460
237
  name,
461
238
  model,
462
239
  summaryModel,
463
- modelProvider,
240
+ modelMiddleware = [],
464
241
  checkpointer,
465
242
  messages,
466
243
  adminUser,
@@ -482,9 +259,7 @@ export async function callAgent(params: {
482
259
 
483
260
  const middleware = [
484
261
  apiBasedToolsMiddleware,
485
- ...(modelProvider === "openai"
486
- ? [createOpenAiResponsesContinuationMiddleware()]
487
- : []),
262
+ ...modelMiddleware,
488
263
  sequenceDebugMiddleware,
489
264
  summarizationMiddleware({
490
265
  model: summaryModel,
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 200,052 bytes received 562 bytes 401,228.00 bytes/sec
42
- total size is 197,748 speedup is 0.99
41
+ sent 200,122 bytes received 558 bytes 401,360.00 bytes/sec
42
+ total size is 197,822 speedup is 0.99
@@ -7,11 +7,11 @@
7
7
  v-for="(part, index) in getMessageParts(message)"
8
8
  :key="part.type"
9
9
  >
10
-
11
10
  <TextRenderer
12
11
  v-if="part.type === 'text'"
13
12
  :message="part.text"
14
13
  :role="props.message.role"
14
+ :state="part.state || props.message.role === 'user' ? 'done' : 'kapets'"
15
15
  />
16
16
  </template>
17
17
 
@@ -18,7 +18,6 @@ var __rest = (this && this.__rest) || function (s, e) {
18
18
  }
19
19
  return t;
20
20
  };
21
- import { convertMessagesToResponsesInput } from "@langchain/openai";
22
21
  import { createMiddleware } from "langchain";
23
22
  import YAML from "yaml";
24
23
  function createPendingSequenceDebug(sequenceId) {
@@ -69,30 +68,18 @@ function finalizeSequenceDebug(sequence) {
69
68
  function getDebugModelName(model) {
70
69
  return typeof model.getName === "function" ? model.getName() : undefined;
71
70
  }
72
- function supportsOpenAiResponseDebug(model) {
73
- return (getDebugModelName(model) === "ChatOpenAI" &&
74
- typeof model.model === "string" &&
75
- typeof model.invocationParams === "function");
76
- }
77
71
  function stringifyPromptForDebug(params) {
78
- var _a, _b, _c, _d;
72
+ var _a, _b, _c;
79
73
  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
- }
88
- return YAML.stringify(Object.assign({ input: convertMessagesToResponsesInput({
89
- messages: [
90
- ...(systemMessage.text === "" ? [] : [systemMessage]),
91
- ...messages,
92
- ],
93
- zdrEnabled: (_d = model.zdrEnabled) !== null && _d !== void 0 ? _d : false,
94
- model: model.model,
95
- }) }, model.invocationParams(Object.assign(Object.assign(Object.assign({}, (modelSettings !== null && modelSettings !== void 0 ? modelSettings : {})), (tools.length > 0 ? { tools } : {})), (toolChoice !== undefined ? { tool_choice: toolChoice } : {})))));
74
+ const invocationParams = typeof model.invocationParams === "function"
75
+ ? model.invocationParams(Object.assign(Object.assign(Object.assign({}, (modelSettings !== null && modelSettings !== void 0 ? modelSettings : {})), (tools.length > 0 ? { tools } : {})), (toolChoice !== undefined ? { tool_choice: toolChoice } : {})))
76
+ : null;
77
+ return YAML.stringify(Object.assign(Object.assign(Object.assign(Object.assign({ model: {
78
+ name: (_a = getDebugModelName(model)) !== null && _a !== void 0 ? _a : null,
79
+ provider: (_c = (_b = model._defaultConfig) === null || _b === void 0 ? void 0 : _b.modelProvider) !== null && _c !== void 0 ? _c : null,
80
+ configuredModel: typeof model.model === "string" ? model.model : null,
81
+ }, systemMessage,
82
+ messages }, (tools.length > 0 ? { tools } : {})), (toolChoice !== undefined ? { toolChoice } : {})), (modelSettings ? { modelSettings } : {})), (invocationParams ? { invocationParams } : {})));
96
83
  }
97
84
  function getMessageBlocks(message) {
98
85
  if (Array.isArray(message.contentBlocks)) {
@@ -8,14 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { createAgent, summarizationMiddleware } from "langchain";
11
- import { MODEL_PROVIDER_CONFIG, getChatModelByClassName, } from "langchain/chat_models/universal";
12
11
  import { logger, } from "adminforth";
13
12
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
14
13
  import { z } from "zod";
15
14
  import { createAgentTools } from "./tools/index.js";
16
15
  import { createApiBasedToolsMiddleware } from "./middleware/apiBasedTools.js";
17
16
  import { createSequenceDebugMiddleware, } from "./middleware/sequenceDebug.js";
18
- import { createOpenAiResponsesContinuationMiddleware } from "./middleware/openAiResponsesContinuation.js";
19
17
  export const contextSchema = z.object({
20
18
  adminUser: z.custom(),
21
19
  userTimeZone: z.string(),
@@ -23,148 +21,22 @@ export const contextSchema = z.object({
23
21
  turnId: z.string(),
24
22
  emitToolCallEvent: z.custom(),
25
23
  });
26
- function isRecord(value) {
27
- return typeof value === "object" && value !== null;
24
+ function isLangChainAgentCompletionAdapter(adapter) {
25
+ return typeof adapter
26
+ .getLangChainAgentSpec === "function";
28
27
  }
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,
28
+ function getAgentChatModelSpec(params) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ var _a;
31
+ const spec = yield params.adapter.getLangChainAgentSpec({
32
+ maxTokens: params.maxTokens,
33
+ purpose: params.purpose,
34
+ });
35
+ return {
36
+ model: spec.model,
37
+ middleware: ((_a = spec.middleware) !== null && _a !== void 0 ? _a : []),
150
38
  };
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 };
39
+ });
168
40
  }
169
41
  function getFiniteNumber(value) {
170
42
  return typeof value === "number" && Number.isFinite(value)
@@ -246,34 +118,25 @@ function createAgentLlmMetricsLogger() {
246
118
  }
247
119
  export function createAgentChatModel(params) {
248
120
  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,
121
+ if (!isLangChainAgentCompletionAdapter(params.adapter)) {
122
+ throw new Error("AdminForth Agent requires completionAdapter to implement getLangChainAgentSpec({ maxTokens, purpose }).");
123
+ }
124
+ return yield getAgentChatModelSpec({
125
+ adapter: params.adapter,
256
126
  maxTokens: params.maxTokens,
127
+ purpose: params.purpose,
257
128
  });
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
129
  });
265
130
  }
266
131
  export function callAgent(params) {
267
132
  return __awaiter(this, void 0, void 0, function* () {
268
- const { name, model, summaryModel, modelProvider, checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
133
+ const { name, model, summaryModel, modelMiddleware = [], checkpointer, messages, adminUser, adminforth, apiBasedTools, customComponentsDir, sessionId, turnId, userTimeZone, emitToolCallEvent, sequenceDebugSink, } = params;
269
134
  const tools = yield createAgentTools(customComponentsDir, apiBasedTools);
270
135
  const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(apiBasedTools, adminforth);
271
136
  const sequenceDebugMiddleware = createSequenceDebugMiddleware(sequenceDebugSink);
272
137
  const middleware = [
273
138
  apiBasedToolsMiddleware,
274
- ...(modelProvider === "openai"
275
- ? [createOpenAiResponsesContinuationMiddleware()]
276
- : []),
139
+ ...modelMiddleware,
277
140
  sequenceDebugMiddleware,
278
141
  summarizationMiddleware({
279
142
  model: summaryModel,
@@ -7,11 +7,11 @@
7
7
  v-for="(part, index) in getMessageParts(message)"
8
8
  :key="part.type"
9
9
  >
10
-
11
10
  <TextRenderer
12
11
  v-if="part.type === 'text'"
13
12
  :message="part.text"
14
13
  :role="props.message.role"
14
+ :state="part.state || props.message.role === 'user' ? 'done' : 'kapets'"
15
15
  />
16
16
  </template>
17
17
 
package/dist/index.js CHANGED
@@ -100,15 +100,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
100
100
  createAgentChatModel({
101
101
  adapter: mode.completionAdapter,
102
102
  maxTokens,
103
+ purpose: "primary",
103
104
  }),
104
105
  createAgentChatModel({
105
106
  adapter: mode.completionAdapter,
106
107
  maxTokens,
108
+ purpose: "summary",
107
109
  }),
108
110
  ]).then(([primaryModel, summaryModel]) => ({
109
111
  model: primaryModel.model,
110
112
  summaryModel: summaryModel.model,
111
- modelProvider: primaryModel.provider,
113
+ modelMiddleware: primaryModel.middleware,
112
114
  }));
113
115
  this.modelsByModeName.set(mode.name, modelsPromise);
114
116
  try {
@@ -284,13 +286,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
284
286
  });
285
287
  const maxTokens = (_f = this.options.maxTokens) !== null && _f !== void 0 ? _f : 10000;
286
288
  const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
287
- const { model, summaryModel, modelProvider } = yield this.getModeModels(selectedMode, maxTokens);
289
+ const { model, summaryModel, modelMiddleware } = yield this.getModeModels(selectedMode, maxTokens);
288
290
  const systemPrompt = yield this.agentSystemPromptPromise;
289
291
  const stream = yield callAgent({
290
292
  name: `adminforth-agent-${this.pluginInstanceId}`,
291
293
  model,
292
294
  summaryModel,
293
- modelProvider,
295
+ modelMiddleware,
294
296
  checkpointer: this.getCheckpointer(),
295
297
  messages: [
296
298
  new SystemMessage(systemPrompt),
package/index.ts CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  createAgentChatModel,
15
15
  callAgent,
16
16
  type AgentChatModel,
17
- type AgentModelProvider,
17
+ type AgentModeCompletionAdapter,
18
18
  } from "./agent/simpleAgent.js";
19
19
  import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
20
20
  import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
@@ -82,7 +82,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
82
82
  Promise<{
83
83
  model: AgentChatModel;
84
84
  summaryModel: AgentChatModel;
85
- modelProvider: AgentModelProvider;
85
+ modelMiddleware: Awaited<ReturnType<typeof createAgentChatModel>>["middleware"];
86
86
  }>
87
87
  >();
88
88
 
@@ -137,15 +137,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
137
137
  createAgentChatModel({
138
138
  adapter: mode.completionAdapter,
139
139
  maxTokens,
140
+ purpose: "primary",
140
141
  }),
141
142
  createAgentChatModel({
142
143
  adapter: mode.completionAdapter,
143
144
  maxTokens,
145
+ purpose: "summary",
144
146
  }),
145
147
  ]).then(([primaryModel, summaryModel]) => ({
146
148
  model: primaryModel.model,
147
149
  summaryModel: summaryModel.model,
148
- modelProvider: primaryModel.provider,
150
+ modelMiddleware: primaryModel.middleware,
149
151
  }));
150
152
 
151
153
  this.modelsByModeName.set(mode.name, modelsPromise);
@@ -341,14 +343,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
341
343
 
342
344
  const maxTokens = this.options.maxTokens ?? 10000;
343
345
  const selectedMode = this.options.modes.find((mode) => mode.name === body.mode) ?? this.options.modes[0];
344
- const { model, summaryModel, modelProvider } =
346
+ const { model, summaryModel, modelMiddleware } =
345
347
  await this.getModeModels(selectedMode, maxTokens);
346
348
  const systemPrompt = await this.agentSystemPromptPromise;
347
349
  const stream = await callAgent({
348
350
  name: `adminforth-agent-${this.pluginInstanceId}`,
349
351
  model,
350
352
  summaryModel,
351
- modelProvider,
353
+ modelMiddleware,
352
354
  checkpointer: this.getCheckpointer(),
353
355
  messages: [
354
356
  new SystemMessage(systemPrompt),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.24.1",
3
+ "version": "1.24.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -30,12 +30,9 @@
30
30
  "typescript": "^5.7.3"
31
31
  },
32
32
  "dependencies": {
33
- "@langchain/anthropic": "1.3.26",
34
33
  "@langchain/core": "^1.1.40",
35
- "@langchain/google-genai": "2.1.27",
36
34
  "@langchain/langgraph": "^1.2.8",
37
35
  "@langchain/langgraph-checkpoint": "^1.0.1",
38
- "@langchain/openai": "^1.4.4",
39
36
  "adminforth": "2.42.0",
40
37
  "dayjs": "^1.11.20",
41
38
  "langchain": "^1.3.3",
package/types.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  type PluginsCommonOptions,
3
- type CompletionAdapter,
4
3
  type AdminUser,
5
4
  type HttpExtra,
6
5
  } from "adminforth";
6
+ import type { AgentModeCompletionAdapter } from "./agent/simpleAgent.js";
7
7
 
8
8
  interface ISessionResource {
9
9
  resourceId: string;
@@ -58,7 +58,7 @@ export interface PluginOptions extends PluginsCommonOptions {
58
58
  */
59
59
  modes: {
60
60
  name: string;
61
- completionAdapter: CompletionAdapter;
61
+ completionAdapter: AgentModeCompletionAdapter;
62
62
  }[];
63
63
 
64
64
  /**