@juspay/neurolink 7.34.0 → 7.36.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/CHANGELOG.md +12 -0
- package/README.md +64 -7
- package/dist/adapters/providerImageAdapter.d.ts +56 -0
- package/dist/adapters/providerImageAdapter.js +257 -0
- package/dist/cli/commands/config.d.ts +20 -20
- package/dist/cli/commands/setup-anthropic.d.ts +16 -0
- package/dist/cli/commands/setup-anthropic.js +414 -0
- package/dist/cli/commands/setup-azure.d.ts +17 -0
- package/dist/cli/commands/setup-azure.js +415 -0
- package/dist/cli/commands/setup-bedrock.d.ts +13 -0
- package/dist/cli/commands/setup-bedrock.js +487 -0
- package/dist/cli/commands/setup-gcp.d.ts +18 -0
- package/dist/cli/commands/setup-gcp.js +569 -0
- package/dist/cli/commands/setup-google-ai.d.ts +16 -0
- package/dist/cli/commands/setup-google-ai.js +369 -0
- package/dist/cli/commands/setup-huggingface.d.ts +8 -0
- package/dist/cli/commands/setup-huggingface.js +200 -0
- package/dist/cli/commands/setup-mistral.d.ts +8 -0
- package/dist/cli/commands/setup-mistral.js +233 -0
- package/dist/cli/commands/setup-openai.d.ts +16 -0
- package/dist/cli/commands/setup-openai.js +402 -0
- package/dist/cli/commands/setup.d.ts +19 -0
- package/dist/cli/commands/setup.js +539 -0
- package/dist/cli/factories/commandFactory.d.ts +5 -0
- package/dist/cli/factories/commandFactory.js +67 -3
- package/dist/cli/factories/setupCommandFactory.d.ts +18 -0
- package/dist/cli/factories/setupCommandFactory.js +137 -0
- package/dist/cli/parser.js +4 -1
- package/dist/cli/utils/envManager.d.ts +3 -2
- package/dist/cli/utils/envManager.js +18 -4
- package/dist/core/baseProvider.js +99 -45
- package/dist/core/types.d.ts +3 -0
- package/dist/lib/adapters/providerImageAdapter.d.ts +56 -0
- package/dist/lib/adapters/providerImageAdapter.js +257 -0
- package/dist/lib/core/baseProvider.js +99 -45
- package/dist/lib/core/types.d.ts +3 -0
- package/dist/lib/neurolink.js +8 -3
- package/dist/lib/types/content.d.ts +78 -0
- package/dist/lib/types/content.js +5 -0
- package/dist/lib/types/conversation.d.ts +19 -0
- package/dist/lib/types/generateTypes.d.ts +4 -1
- package/dist/lib/types/streamTypes.d.ts +6 -3
- package/dist/lib/utils/imageProcessor.d.ts +84 -0
- package/dist/lib/utils/imageProcessor.js +362 -0
- package/dist/lib/utils/messageBuilder.d.ts +8 -1
- package/dist/lib/utils/messageBuilder.js +279 -0
- package/dist/neurolink.js +8 -3
- package/dist/types/content.d.ts +78 -0
- package/dist/types/content.js +5 -0
- package/dist/types/conversation.d.ts +19 -0
- package/dist/types/generateTypes.d.ts +4 -1
- package/dist/types/streamTypes.d.ts +6 -3
- package/dist/utils/imageProcessor.d.ts +84 -0
- package/dist/utils/imageProcessor.js +362 -0
- package/dist/utils/messageBuilder.d.ts +8 -1
- package/dist/utils/messageBuilder.js +279 -0
- package/package.json +1 -1
@@ -0,0 +1,137 @@
|
|
1
|
+
/**
|
2
|
+
* Setup Command Factory for NeuroLink
|
3
|
+
* Consolidates all provider setup commands into a unified interface
|
4
|
+
*/
|
5
|
+
import { handleGCPSetup } from "../commands/setup-gcp.js";
|
6
|
+
import { handleBedrockSetup } from "../commands/setup-bedrock.js";
|
7
|
+
import { handleOpenAISetup } from "../commands/setup-openai.js";
|
8
|
+
import { handleGoogleAISetup } from "../commands/setup-google-ai.js";
|
9
|
+
import { handleAnthropicSetup } from "../commands/setup-anthropic.js";
|
10
|
+
import { handleAzureSetup } from "../commands/setup-azure.js";
|
11
|
+
import { handleHuggingFaceSetup } from "../commands/setup-huggingface.js";
|
12
|
+
import { handleMistralSetup } from "../commands/setup-mistral.js";
|
13
|
+
import { handleSetup } from "../commands/setup.js";
|
14
|
+
/**
|
15
|
+
* Setup Command Factory
|
16
|
+
*/
|
17
|
+
export class SetupCommandFactory {
|
18
|
+
/**
|
19
|
+
* Create the main setup command with all provider subcommands
|
20
|
+
*/
|
21
|
+
static createSetupCommands() {
|
22
|
+
return {
|
23
|
+
command: ["setup [provider]", "s [provider]"],
|
24
|
+
describe: "Setup AI provider configurations",
|
25
|
+
builder: (yargs) => {
|
26
|
+
return (yargs
|
27
|
+
.positional("provider", {
|
28
|
+
type: "string",
|
29
|
+
description: "Specific provider to set up",
|
30
|
+
choices: [
|
31
|
+
"google-ai",
|
32
|
+
"openai",
|
33
|
+
"anthropic",
|
34
|
+
"azure",
|
35
|
+
"bedrock",
|
36
|
+
"gcp",
|
37
|
+
"vertex",
|
38
|
+
"huggingface",
|
39
|
+
"mistral",
|
40
|
+
],
|
41
|
+
})
|
42
|
+
.option("list", {
|
43
|
+
type: "boolean",
|
44
|
+
description: "List all available providers",
|
45
|
+
alias: "l",
|
46
|
+
})
|
47
|
+
.option("status", {
|
48
|
+
type: "boolean",
|
49
|
+
description: "Show provider configuration status",
|
50
|
+
})
|
51
|
+
.option("check", {
|
52
|
+
type: "boolean",
|
53
|
+
description: "Only check existing configuration without prompting",
|
54
|
+
default: false,
|
55
|
+
})
|
56
|
+
.option("non-interactive", {
|
57
|
+
type: "boolean",
|
58
|
+
description: "Skip interactive prompts",
|
59
|
+
default: false,
|
60
|
+
})
|
61
|
+
.option("quiet", {
|
62
|
+
type: "boolean",
|
63
|
+
alias: "q",
|
64
|
+
default: false,
|
65
|
+
description: "Suppress non-essential output",
|
66
|
+
})
|
67
|
+
.option("debug", {
|
68
|
+
type: "boolean",
|
69
|
+
default: false,
|
70
|
+
description: "Enable debug output",
|
71
|
+
})
|
72
|
+
// Subcommands for each provider
|
73
|
+
.command("google-ai", "Setup Google AI Studio configuration", (y) => this.buildProviderOptions(y),
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
75
|
+
async (argv) => await handleGoogleAISetup(argv))
|
76
|
+
.command("openai", "Setup OpenAI configuration", (y) => this.buildProviderOptions(y),
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
78
|
+
async (argv) => await handleOpenAISetup(argv))
|
79
|
+
.command("anthropic", "Setup Anthropic Claude configuration", (y) => this.buildProviderOptions(y),
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
81
|
+
async (argv) => await handleAnthropicSetup(argv))
|
82
|
+
.command("azure", "Setup Azure OpenAI configuration", (y) => this.buildProviderOptions(y),
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
84
|
+
async (argv) => await handleAzureSetup(argv))
|
85
|
+
.command("bedrock", "Setup AWS Bedrock configuration", (y) => this.buildProviderOptions(y),
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
87
|
+
async (argv) => await handleBedrockSetup(argv))
|
88
|
+
.command(["gcp", "vertex"], "Setup Google Cloud Platform / Vertex AI configuration", (y) => this.buildProviderOptions(y),
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
90
|
+
async (argv) => await handleGCPSetup(argv))
|
91
|
+
.command("huggingface", "Setup Hugging Face configuration", (y) => this.buildProviderOptions(y),
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
93
|
+
async (argv) => await handleHuggingFaceSetup(argv))
|
94
|
+
.command("mistral", "Setup Mistral AI configuration", (y) => this.buildProviderOptions(y),
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
96
|
+
async (argv) => await handleMistralSetup(argv))
|
97
|
+
.example("$0 setup", "Interactive setup wizard")
|
98
|
+
.example("$0 setup google-ai", "Setup Google AI Studio")
|
99
|
+
.example("$0 setup openai --check", "Check OpenAI configuration")
|
100
|
+
.example("$0 setup --list", "List all providers")
|
101
|
+
.example("$0 setup --status", "Check provider status")
|
102
|
+
.help());
|
103
|
+
},
|
104
|
+
handler: async (argv) => {
|
105
|
+
// If no subcommand specified, run main setup wizard
|
106
|
+
await handleSetup(argv);
|
107
|
+
},
|
108
|
+
};
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* Build common options for provider setup commands
|
112
|
+
*/
|
113
|
+
static buildProviderOptions(yargs) {
|
114
|
+
return yargs
|
115
|
+
.option("check", {
|
116
|
+
type: "boolean",
|
117
|
+
describe: "Only check existing configuration without prompting",
|
118
|
+
default: false,
|
119
|
+
})
|
120
|
+
.option("non-interactive", {
|
121
|
+
type: "boolean",
|
122
|
+
describe: "Skip interactive prompts",
|
123
|
+
default: false,
|
124
|
+
})
|
125
|
+
.option("quiet", {
|
126
|
+
type: "boolean",
|
127
|
+
alias: "q",
|
128
|
+
default: false,
|
129
|
+
description: "Suppress non-essential output",
|
130
|
+
})
|
131
|
+
.option("debug", {
|
132
|
+
type: "boolean",
|
133
|
+
default: false,
|
134
|
+
description: "Enable debug output",
|
135
|
+
});
|
136
|
+
}
|
137
|
+
}
|
package/dist/cli/parser.js
CHANGED
@@ -6,6 +6,7 @@ import { CLICommandFactory } from "./factories/commandFactory.js";
|
|
6
6
|
import { globalSession } from "../lib/session/globalSessionState.js";
|
7
7
|
import { handleError } from "./errorHandler.js";
|
8
8
|
import { logger } from "../lib/utils/logger.js";
|
9
|
+
import { SetupCommandFactory } from "./factories/setupCommandFactory.js";
|
9
10
|
// Enhanced CLI with Professional UX
|
10
11
|
export function initializeCliParser() {
|
11
12
|
return (yargs(hideBin(process.argv))
|
@@ -154,5 +155,7 @@ export function initializeCliParser() {
|
|
154
155
|
// SageMaker Command Group - Using CLICommandFactory
|
155
156
|
.command(CLICommandFactory.createSageMakerCommands())
|
156
157
|
// Loop Command - Using CLICommandFactory
|
157
|
-
.command(CLICommandFactory.createLoopCommand())
|
158
|
+
.command(CLICommandFactory.createLoopCommand())
|
159
|
+
// Setup Commands - Using SetupCommandFactory
|
160
|
+
.command(SetupCommandFactory.createSetupCommands())); // Close the main return statement
|
158
161
|
}
|
@@ -12,6 +12,7 @@ export interface EnvUpdateResult {
|
|
12
12
|
updated: string[];
|
13
13
|
added: string[];
|
14
14
|
unchanged: string[];
|
15
|
+
deleted: string[];
|
15
16
|
}
|
16
17
|
/**
|
17
18
|
* Create a timestamped backup of the existing .env file
|
@@ -24,11 +25,11 @@ export declare function parseEnvFile(content: string): Record<string, string>;
|
|
24
25
|
/**
|
25
26
|
* Generate .env file content from key-value pairs
|
26
27
|
*/
|
27
|
-
export declare function generateEnvContent(envVars: Record<string, string>, existingContent?: string): string;
|
28
|
+
export declare function generateEnvContent(envVars: Record<string, string>, existingContent?: string, keysToDelete?: string[]): string;
|
28
29
|
/**
|
29
30
|
* Update .env file with new environment variables
|
30
31
|
*/
|
31
|
-
export declare function updateEnvFile(newVars: Record<string, string>, envPath?: string, createBackup?: boolean): EnvUpdateResult;
|
32
|
+
export declare function updateEnvFile(newVars: Record<string, string>, envPath?: string, createBackup?: boolean, keysToDelete?: string[]): EnvUpdateResult;
|
32
33
|
/**
|
33
34
|
* Display environment file update summary
|
34
35
|
*/
|
@@ -59,7 +59,7 @@ export function parseEnvFile(content) {
|
|
59
59
|
/**
|
60
60
|
* Generate .env file content from key-value pairs
|
61
61
|
*/
|
62
|
-
export function generateEnvContent(envVars, existingContent) {
|
62
|
+
export function generateEnvContent(envVars, existingContent, keysToDelete = []) {
|
63
63
|
const lines = [];
|
64
64
|
const _existingVars = existingContent ? parseEnvFile(existingContent) : {};
|
65
65
|
const processedKeys = new Set();
|
@@ -79,6 +79,10 @@ export function generateEnvContent(envVars, existingContent) {
|
|
79
79
|
continue;
|
80
80
|
}
|
81
81
|
const key = trimmedLine.substring(0, equalIndex).trim();
|
82
|
+
// Skip keys that should be deleted
|
83
|
+
if (keysToDelete.includes(key)) {
|
84
|
+
continue; // Skip this line - delete the key
|
85
|
+
}
|
82
86
|
if (Object.prototype.hasOwnProperty.call(envVars, key)) {
|
83
87
|
// Update existing variable
|
84
88
|
lines.push(`${key}=${envVars[key]}`);
|
@@ -108,12 +112,13 @@ export function generateEnvContent(envVars, existingContent) {
|
|
108
112
|
/**
|
109
113
|
* Update .env file with new environment variables
|
110
114
|
*/
|
111
|
-
export function updateEnvFile(newVars, envPath = ".env", createBackup = true) {
|
115
|
+
export function updateEnvFile(newVars, envPath = ".env", createBackup = true, keysToDelete = []) {
|
112
116
|
const result = {
|
113
117
|
backup: { existed: false },
|
114
118
|
updated: [],
|
115
119
|
added: [],
|
116
120
|
unchanged: [],
|
121
|
+
deleted: [],
|
117
122
|
};
|
118
123
|
// Create backup if requested and file exists
|
119
124
|
if (createBackup) {
|
@@ -126,6 +131,12 @@ export function updateEnvFile(newVars, envPath = ".env", createBackup = true) {
|
|
126
131
|
existingContent = fs.readFileSync(envPath, "utf8");
|
127
132
|
_existingVars = parseEnvFile(existingContent);
|
128
133
|
}
|
134
|
+
// Track keys to be deleted
|
135
|
+
for (const key of keysToDelete) {
|
136
|
+
if (Object.prototype.hasOwnProperty.call(_existingVars, key)) {
|
137
|
+
result.deleted.push(key);
|
138
|
+
}
|
139
|
+
}
|
129
140
|
// Categorize changes
|
130
141
|
for (const [key, value] of Object.entries(newVars)) {
|
131
142
|
if (Object.prototype.hasOwnProperty.call(_existingVars, key)) {
|
@@ -141,7 +152,7 @@ export function updateEnvFile(newVars, envPath = ".env", createBackup = true) {
|
|
141
152
|
}
|
142
153
|
}
|
143
154
|
// Generate new content
|
144
|
-
const newContent = generateEnvContent(newVars, existingContent);
|
155
|
+
const newContent = generateEnvContent(newVars, existingContent, keysToDelete);
|
145
156
|
// Write updated file
|
146
157
|
try {
|
147
158
|
fs.writeFileSync(envPath, newContent, "utf8");
|
@@ -167,10 +178,13 @@ export function displayEnvUpdateSummary(result, quiet = false) {
|
|
167
178
|
if (result.updated.length > 0) {
|
168
179
|
logger.always(chalk.yellow(`🔄 Updated ${result.updated.length} existing variables: ${result.updated.join(", ")}`));
|
169
180
|
}
|
181
|
+
if (result.deleted.length > 0) {
|
182
|
+
logger.always(chalk.red(`🗑️ Deleted ${result.deleted.length} variables: ${result.deleted.join(", ")}`));
|
183
|
+
}
|
170
184
|
if (result.unchanged.length > 0) {
|
171
185
|
logger.always(chalk.gray(`✓ ${result.unchanged.length} variables unchanged: ${result.unchanged.join(", ")}`));
|
172
186
|
}
|
173
|
-
const totalChanges = result.added.length + result.updated.length;
|
187
|
+
const totalChanges = result.added.length + result.updated.length + result.deleted.length;
|
174
188
|
if (totalChanges > 0) {
|
175
189
|
logger.always(chalk.blue(`📝 Environment file updated with ${totalChanges} changes`));
|
176
190
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { generateText } from "ai";
|
1
2
|
import { MiddlewareFactory } from "../middleware/factory.js";
|
2
3
|
import { logger } from "../utils/logger.js";
|
3
4
|
import { DEFAULT_MAX_STEPS, STEP_LIMITS } from "../core/constants.js";
|
@@ -5,12 +6,11 @@ import { directAgentTools } from "../agent/directTools.js";
|
|
5
6
|
import { getSafeMaxTokens } from "../utils/tokenLimits.js";
|
6
7
|
import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
|
7
8
|
import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
|
8
|
-
import { buildMessagesArray } from "../utils/messageBuilder.js";
|
9
|
+
import { buildMessagesArray, buildMultimodalMessagesArray, } from "../utils/messageBuilder.js";
|
9
10
|
import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
|
10
11
|
import { validateStreamOptions as validateStreamOpts, validateTextGenerationOptions, ValidationError, createValidationSummary, } from "../utils/parameterValidation.js";
|
11
12
|
import { recordProviderPerformanceFromMetrics, getPerformanceOptimizedProvider, } from "./evaluationProviders.js";
|
12
13
|
import { modelConfig } from "./modelConfiguration.js";
|
13
|
-
// Provider types moved to ../types/providers.js
|
14
14
|
/**
|
15
15
|
* Abstract base class for all AI providers
|
16
16
|
* Tools are integrated as first-class citizens - always available by default
|
@@ -166,7 +166,7 @@ export class BaseProvider {
|
|
166
166
|
try {
|
167
167
|
// Import streamText dynamically to avoid circular dependencies
|
168
168
|
// Using streamText instead of generateText for unified implementation
|
169
|
-
const { streamText } = await import("ai");
|
169
|
+
// const { streamText } = await import("ai");
|
170
170
|
// Get ALL available tools (direct + MCP + external from options)
|
171
171
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
172
172
|
const baseTools = shouldUseTools ? await this.getAllTools() : {};
|
@@ -211,42 +211,86 @@ export class BaseProvider {
|
|
211
211
|
});
|
212
212
|
const model = await this.getAISDKModelWithMiddleware(options);
|
213
213
|
// Build proper message array with conversation history
|
214
|
-
|
215
|
-
|
216
|
-
|
214
|
+
// Check if this is a multimodal request (images or content present)
|
215
|
+
let messages;
|
216
|
+
// Type guard to check if options has multimodal input
|
217
|
+
const hasMultimodalInput = (opts) => {
|
218
|
+
const input = opts.input;
|
219
|
+
const hasImages = !!input?.images?.length;
|
220
|
+
const hasContent = !!input?.content?.length;
|
221
|
+
return hasImages || hasContent;
|
222
|
+
};
|
223
|
+
if (hasMultimodalInput(options)) {
|
224
|
+
if (process.env.NEUROLINK_DEBUG === "true") {
|
225
|
+
logger.info("🖼️ [MULTIMODAL-REQUEST] Detected multimodal input, using multimodal message builder");
|
226
|
+
}
|
227
|
+
// This is a multimodal request - use multimodal message builder
|
228
|
+
// Convert TextGenerationOptions to GenerateOptions format for multimodal processing
|
229
|
+
const input = options.input;
|
230
|
+
const multimodalOptions = {
|
231
|
+
input: {
|
232
|
+
text: options.prompt || options.input?.text || "",
|
233
|
+
images: input?.images,
|
234
|
+
content: input?.content,
|
235
|
+
},
|
236
|
+
provider: options.provider,
|
237
|
+
model: options.model,
|
238
|
+
temperature: options.temperature,
|
239
|
+
maxTokens: options.maxTokens,
|
240
|
+
systemPrompt: options.systemPrompt,
|
241
|
+
enableAnalytics: options.enableAnalytics,
|
242
|
+
enableEvaluation: options.enableEvaluation,
|
243
|
+
context: options.context,
|
244
|
+
};
|
245
|
+
messages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
246
|
+
}
|
247
|
+
else {
|
248
|
+
if (process.env.NEUROLINK_DEBUG === "true") {
|
249
|
+
logger.info("📝 [TEXT-ONLY-REQUEST] No multimodal input detected, using standard message builder");
|
250
|
+
}
|
251
|
+
// Standard text-only request
|
252
|
+
messages = buildMessagesArray(options);
|
253
|
+
}
|
254
|
+
// Convert messages to Vercel AI SDK format
|
255
|
+
const aiSDKMessages = messages.map((msg) => {
|
256
|
+
if (typeof msg.content === "string") {
|
257
|
+
// Simple text content
|
258
|
+
return {
|
259
|
+
role: msg.role,
|
260
|
+
content: msg.content,
|
261
|
+
};
|
262
|
+
}
|
263
|
+
else {
|
264
|
+
// Multimodal content array - convert to Vercel AI SDK format
|
265
|
+
// The Vercel AI SDK expects content to be in a specific format
|
266
|
+
return {
|
267
|
+
role: msg.role,
|
268
|
+
content: msg.content.map((item) => {
|
269
|
+
if (item.type === "text") {
|
270
|
+
return { type: "text", text: item.text || "" };
|
271
|
+
}
|
272
|
+
else if (item.type === "image") {
|
273
|
+
return { type: "image", image: item.image || "" };
|
274
|
+
}
|
275
|
+
return item;
|
276
|
+
}),
|
277
|
+
};
|
278
|
+
}
|
279
|
+
});
|
280
|
+
const generateResult = await generateText({
|
217
281
|
model,
|
218
|
-
messages:
|
282
|
+
messages: aiSDKMessages,
|
219
283
|
tools,
|
220
284
|
maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
|
221
285
|
toolChoice: shouldUseTools ? "auto" : "none",
|
222
286
|
temperature: options.temperature,
|
223
287
|
maxTokens: options.maxTokens, // No default limit - unlimited unless specified
|
224
288
|
});
|
225
|
-
// Accumulate the streamed content
|
226
|
-
let accumulatedContent = "";
|
227
|
-
// Wait for the stream to complete and accumulate content
|
228
|
-
try {
|
229
|
-
for await (const chunk of streamResult.textStream) {
|
230
|
-
accumulatedContent += chunk;
|
231
|
-
}
|
232
|
-
}
|
233
|
-
catch (streamError) {
|
234
|
-
logger.error(`Error reading text stream for ${this.providerName}:`, streamError);
|
235
|
-
throw streamError;
|
236
|
-
}
|
237
|
-
// Get the final result - this should include usage, toolCalls, etc.
|
238
|
-
const usage = await streamResult.usage;
|
239
|
-
const toolCalls = await streamResult.toolCalls;
|
240
|
-
const toolResults = await streamResult.toolResults;
|
241
289
|
const responseTime = Date.now() - startTime;
|
242
|
-
//
|
243
|
-
const
|
244
|
-
|
245
|
-
|
246
|
-
toolCalls: toolCalls,
|
247
|
-
toolResults: toolResults,
|
248
|
-
steps: streamResult.steps, // Include steps for tool execution tracking
|
249
|
-
};
|
290
|
+
// Extract properties from generateResult
|
291
|
+
const usage = generateResult.usage;
|
292
|
+
const toolCalls = generateResult.toolCalls;
|
293
|
+
const toolResults = generateResult.toolResults;
|
250
294
|
try {
|
251
295
|
const actualCost = await this.calculateActualCost(usage || { promptTokens: 0, completionTokens: 0, totalTokens: 0 });
|
252
296
|
recordProviderPerformanceFromMetrics(this.providerName, {
|
@@ -273,14 +317,14 @@ export class BaseProvider {
|
|
273
317
|
// First check direct tool calls (fallback)
|
274
318
|
if (toolCalls && toolCalls.length > 0) {
|
275
319
|
toolsUsed.push(...toolCalls.map((tc) => {
|
276
|
-
return tc.toolName || "unknown";
|
320
|
+
return tc.toolName || tc.name || "unknown";
|
277
321
|
}));
|
278
322
|
}
|
279
323
|
// Then check steps for tool calls (primary source for multi-step)
|
280
|
-
if (
|
281
|
-
Array.isArray(
|
282
|
-
for (const step of
|
283
|
-
[]) {
|
324
|
+
if (generateResult.steps &&
|
325
|
+
Array.isArray(generateResult.steps)) {
|
326
|
+
for (const step of generateResult
|
327
|
+
.steps || []) {
|
284
328
|
if (step?.toolCalls && Array.isArray(step.toolCalls)) {
|
285
329
|
toolsUsed.push(...step.toolCalls.map((tc) => {
|
286
330
|
return tc.toolName || tc.name || "unknown";
|
@@ -295,10 +339,10 @@ export class BaseProvider {
|
|
295
339
|
// Create a map of tool calls to their arguments for matching with results
|
296
340
|
const toolCallArgsMap = new Map();
|
297
341
|
// Extract tool executions from AI SDK result steps
|
298
|
-
if (
|
299
|
-
Array.isArray(
|
300
|
-
for (const step of
|
301
|
-
[]) {
|
342
|
+
if (generateResult.steps &&
|
343
|
+
Array.isArray(generateResult.steps)) {
|
344
|
+
for (const step of generateResult
|
345
|
+
.steps || []) {
|
302
346
|
// First, collect tool calls and their arguments
|
303
347
|
if (step?.toolCalls && Array.isArray(step.toolCalls)) {
|
304
348
|
for (const toolCall of step.toolCalls) {
|
@@ -359,11 +403,11 @@ export class BaseProvider {
|
|
359
403
|
}
|
360
404
|
// Format the result with tool executions included
|
361
405
|
const enhancedResult = {
|
362
|
-
content:
|
406
|
+
content: generateResult.text,
|
363
407
|
usage: {
|
364
|
-
input:
|
365
|
-
output:
|
366
|
-
total:
|
408
|
+
input: generateResult.usage?.promptTokens || 0,
|
409
|
+
output: generateResult.usage?.completionTokens || 0,
|
410
|
+
total: generateResult.usage?.totalTokens || 0,
|
367
411
|
},
|
368
412
|
provider: this.providerName,
|
369
413
|
model: this.modelName,
|
@@ -943,13 +987,23 @@ export class BaseProvider {
|
|
943
987
|
const providerName = optionsOrPrompt.provider || this.providerName;
|
944
988
|
// Apply safe maxTokens based on provider and model
|
945
989
|
const safeMaxTokens = getSafeMaxTokens(providerName, modelName, optionsOrPrompt.maxTokens);
|
946
|
-
|
990
|
+
// CRITICAL FIX: Preserve the entire input object for multimodal support
|
991
|
+
// This ensures images and content arrays are not lost during normalization
|
992
|
+
const normalizedOptions = {
|
947
993
|
...optionsOrPrompt,
|
948
994
|
prompt,
|
949
995
|
provider: providerName,
|
950
996
|
model: modelName,
|
951
997
|
maxTokens: safeMaxTokens,
|
952
998
|
};
|
999
|
+
// Ensure input object is preserved if it exists (for multimodal support)
|
1000
|
+
if (optionsOrPrompt.input) {
|
1001
|
+
normalizedOptions.input = {
|
1002
|
+
...optionsOrPrompt.input,
|
1003
|
+
text: prompt, // Ensure text is consistent
|
1004
|
+
};
|
1005
|
+
}
|
1006
|
+
return normalizedOptions;
|
953
1007
|
}
|
954
1008
|
normalizeStreamOptions(optionsOrPrompt) {
|
955
1009
|
if (typeof optionsOrPrompt === "string") {
|
package/dist/core/types.d.ts
CHANGED
@@ -175,11 +175,14 @@ export interface StreamingOptions {
|
|
175
175
|
}
|
176
176
|
/**
|
177
177
|
* Text generation options interface
|
178
|
+
* Extended to support multimodal content with zero breaking changes
|
178
179
|
*/
|
179
180
|
export interface TextGenerationOptions {
|
180
181
|
prompt?: string;
|
181
182
|
input?: {
|
182
183
|
text: string;
|
184
|
+
images?: Array<Buffer | string>;
|
185
|
+
content?: Array<import("../types/content.js").TextContent | import("../types/content.js").ImageContent>;
|
183
186
|
};
|
184
187
|
provider?: AIProviderName;
|
185
188
|
model?: string;
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/**
|
2
|
+
* Provider Image Adapter - Smart routing for multimodal content
|
3
|
+
* Handles provider-specific image formatting and vision capability validation
|
4
|
+
*/
|
5
|
+
import type { Content } from "../types/content.js";
|
6
|
+
/**
|
7
|
+
* Simplified logger for essential error reporting only
|
8
|
+
*/
|
9
|
+
export declare class MultimodalLogger {
|
10
|
+
static logError(step: string, error: Error, context: unknown): void;
|
11
|
+
}
|
12
|
+
/**
|
13
|
+
* Provider Image Adapter - Smart routing and formatting
|
14
|
+
*/
|
15
|
+
export declare class ProviderImageAdapter {
|
16
|
+
/**
|
17
|
+
* Main adapter method - routes to provider-specific formatting
|
18
|
+
*/
|
19
|
+
static adaptForProvider(text: string, images: Array<Buffer | string>, provider: string, model: string): Promise<unknown>;
|
20
|
+
/**
|
21
|
+
* Format content for OpenAI (GPT-4o format)
|
22
|
+
*/
|
23
|
+
private static formatForOpenAI;
|
24
|
+
/**
|
25
|
+
* Format content for Google AI (Gemini format)
|
26
|
+
*/
|
27
|
+
private static formatForGoogleAI;
|
28
|
+
/**
|
29
|
+
* Format content for Anthropic (Claude format)
|
30
|
+
*/
|
31
|
+
private static formatForAnthropic;
|
32
|
+
/**
|
33
|
+
* Format content for Vertex AI (model-specific routing)
|
34
|
+
*/
|
35
|
+
private static formatForVertex;
|
36
|
+
/**
|
37
|
+
* Validate that provider and model support vision
|
38
|
+
*/
|
39
|
+
private static validateVisionSupport;
|
40
|
+
/**
|
41
|
+
* Convert simple images array to advanced content format
|
42
|
+
*/
|
43
|
+
static convertToContent(text: string, images?: Array<Buffer | string>): Content[];
|
44
|
+
/**
|
45
|
+
* Check if provider supports multimodal content
|
46
|
+
*/
|
47
|
+
static supportsVision(provider: string, model?: string): boolean;
|
48
|
+
/**
|
49
|
+
* Get supported models for a provider
|
50
|
+
*/
|
51
|
+
static getSupportedModels(provider: string): string[];
|
52
|
+
/**
|
53
|
+
* Get all vision-capable providers
|
54
|
+
*/
|
55
|
+
static getVisionProviders(): string[];
|
56
|
+
}
|