@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +64 -7
  3. package/dist/adapters/providerImageAdapter.d.ts +56 -0
  4. package/dist/adapters/providerImageAdapter.js +257 -0
  5. package/dist/cli/commands/config.d.ts +20 -20
  6. package/dist/cli/commands/setup-anthropic.d.ts +16 -0
  7. package/dist/cli/commands/setup-anthropic.js +414 -0
  8. package/dist/cli/commands/setup-azure.d.ts +17 -0
  9. package/dist/cli/commands/setup-azure.js +415 -0
  10. package/dist/cli/commands/setup-bedrock.d.ts +13 -0
  11. package/dist/cli/commands/setup-bedrock.js +487 -0
  12. package/dist/cli/commands/setup-gcp.d.ts +18 -0
  13. package/dist/cli/commands/setup-gcp.js +569 -0
  14. package/dist/cli/commands/setup-google-ai.d.ts +16 -0
  15. package/dist/cli/commands/setup-google-ai.js +369 -0
  16. package/dist/cli/commands/setup-huggingface.d.ts +8 -0
  17. package/dist/cli/commands/setup-huggingface.js +200 -0
  18. package/dist/cli/commands/setup-mistral.d.ts +8 -0
  19. package/dist/cli/commands/setup-mistral.js +233 -0
  20. package/dist/cli/commands/setup-openai.d.ts +16 -0
  21. package/dist/cli/commands/setup-openai.js +402 -0
  22. package/dist/cli/commands/setup.d.ts +19 -0
  23. package/dist/cli/commands/setup.js +539 -0
  24. package/dist/cli/factories/commandFactory.d.ts +5 -0
  25. package/dist/cli/factories/commandFactory.js +67 -3
  26. package/dist/cli/factories/setupCommandFactory.d.ts +18 -0
  27. package/dist/cli/factories/setupCommandFactory.js +137 -0
  28. package/dist/cli/parser.js +4 -1
  29. package/dist/cli/utils/envManager.d.ts +3 -2
  30. package/dist/cli/utils/envManager.js +18 -4
  31. package/dist/core/baseProvider.js +99 -45
  32. package/dist/core/types.d.ts +3 -0
  33. package/dist/lib/adapters/providerImageAdapter.d.ts +56 -0
  34. package/dist/lib/adapters/providerImageAdapter.js +257 -0
  35. package/dist/lib/core/baseProvider.js +99 -45
  36. package/dist/lib/core/types.d.ts +3 -0
  37. package/dist/lib/neurolink.js +8 -3
  38. package/dist/lib/types/content.d.ts +78 -0
  39. package/dist/lib/types/content.js +5 -0
  40. package/dist/lib/types/conversation.d.ts +19 -0
  41. package/dist/lib/types/generateTypes.d.ts +4 -1
  42. package/dist/lib/types/streamTypes.d.ts +6 -3
  43. package/dist/lib/utils/imageProcessor.d.ts +84 -0
  44. package/dist/lib/utils/imageProcessor.js +362 -0
  45. package/dist/lib/utils/messageBuilder.d.ts +8 -1
  46. package/dist/lib/utils/messageBuilder.js +279 -0
  47. package/dist/neurolink.js +8 -3
  48. package/dist/types/content.d.ts +78 -0
  49. package/dist/types/content.js +5 -0
  50. package/dist/types/conversation.d.ts +19 -0
  51. package/dist/types/generateTypes.d.ts +4 -1
  52. package/dist/types/streamTypes.d.ts +6 -3
  53. package/dist/utils/imageProcessor.d.ts +84 -0
  54. package/dist/utils/imageProcessor.js +362 -0
  55. package/dist/utils/messageBuilder.d.ts +8 -1
  56. package/dist/utils/messageBuilder.js +279 -0
  57. 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
+ }
@@ -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
- const messages = buildMessagesArray(options);
215
- // Use streamText and accumulate results instead of generateText
216
- const streamResult = await streamText({
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: 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
- // Create a result object compatible with generateText format
243
- const result = {
244
- text: accumulatedContent,
245
- usage: usage,
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 (result.steps &&
281
- Array.isArray(result.steps)) {
282
- for (const step of result.steps ||
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 (result.steps &&
299
- Array.isArray(result.steps)) {
300
- for (const step of result.steps ||
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: result.text,
406
+ content: generateResult.text,
363
407
  usage: {
364
- input: result.usage?.promptTokens || 0,
365
- output: result.usage?.completionTokens || 0,
366
- total: result.usage?.totalTokens || 0,
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
- return {
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") {
@@ -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
+ }