npm-ai-hooks 2.0.0 → 2.0.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 CHANGED
@@ -4,12 +4,15 @@
4
4
 
5
5
  Inject LLM-like behavior into any JavaScript or TypeScript function with a single line, without writing prompts, handling SDKs, or locking into any provider. Works seamlessly in both Node.js (Express) and React (Vite) environments.
6
6
 
7
+ 📚 **[View Homepage & Demos](./website/)** | 🎮 **[React Example](./examples/react/)** | 💻 **[Node.js Example](./examples/demo.ts)**
8
+
7
9
  ---
8
10
 
9
11
  ## Features
10
12
 
11
13
  * **Universal API:** Works with OpenAI, Claude, Gemini, DeepSeek, Groq, OpenRouter, XAI, Perplexity, and Mistral — out of the box.
12
14
  * **Cross-Platform:** Works in both Node.js (Express) and React (Vite) environments with dual build system.
15
+ * **Multimodal Support:** 🆕 Images, files, and voice input support with vision-enabled models.
13
16
  * **Plug & Play:** Wrap any function and instantly give it AI-powered behavior.
14
17
  * **Zero Prompting:** Built-in task templates (summarize, explain, translate, sentiment, rewrite, code-review, etc.)
15
18
  * **Explicit Configuration:** No environment variables needed - initialize providers explicitly with API keys.
@@ -20,7 +23,7 @@ Inject LLM-like behavior into any JavaScript or TypeScript function with a singl
20
23
  * **Cost Awareness:** Estimate and log token usage and cost before and after calls.
21
24
  * **Caching:** Prevents duplicate calls and charges by caching results intelligently.
22
25
  * **Extensible:** Add your own providers and custom tasks easily.
23
- * **Debug Friendly:** Full debug logging with `AI_HOOK_DEBUG=true`.
26
+ * **Debug Friendly:** Full debug logging with `DEBUG=true`.
24
27
 
25
28
  ---
26
29
 
@@ -230,7 +233,106 @@ const result = await translate(await summarize("Long technical article..."));
230
233
  console.log(result); // Résumé en français
231
234
  ```
232
235
 
233
- ### 4. Error Handling
236
+ ### 4. Multimodal Support (Images, Files, Voice) 🎨🎤
237
+
238
+ **NEW!** npm-ai-hooks now supports multimodal inputs including images, files, and voice.
239
+
240
+ ```typescript
241
+ import { wrap, MultimodalInput } from "npm-ai-hooks";
242
+ import * as fs from 'fs';
243
+
244
+ // Image Analysis
245
+ const analyzeImage = wrap((input: MultimodalInput) => input, {
246
+ provider: 'openai',
247
+ model: 'gpt-4o', // Vision-enabled model
248
+ customPrompt: 'Describe what you see in this image'
249
+ });
250
+
251
+ // Load and encode image
252
+ const imageBuffer = fs.readFileSync('./photo.jpg');
253
+ const base64Image = `data:image/jpeg;base64,${imageBuffer.toString('base64')}`;
254
+
255
+ const result = await analyzeImage({
256
+ text: 'What is in this image?',
257
+ image: base64Image
258
+ });
259
+ console.log(result.output); // Detailed image description
260
+
261
+ // OCR (Text Extraction)
262
+ const extractText = wrap((input: MultimodalInput) => input, {
263
+ provider: 'openai',
264
+ model: 'gpt-4o',
265
+ customPrompt: 'Extract all text from this image'
266
+ });
267
+
268
+ const ocrResult = await extractText({
269
+ text: 'Extract text from this document',
270
+ image: base64Image
271
+ });
272
+
273
+ // File Processing
274
+ const analyzeFile = wrap((input: MultimodalInput) => input, {
275
+ provider: 'claude',
276
+ model: 'claude-3-opus',
277
+ task: 'summarize'
278
+ });
279
+
280
+ const fileBuffer = fs.readFileSync('./document.pdf');
281
+ const fileResult = await analyzeFile({
282
+ text: 'Summarize this document',
283
+ file: {
284
+ name: 'document.pdf',
285
+ data: `data:application/pdf;base64,${fileBuffer.toString('base64')}`,
286
+ type: 'application/pdf'
287
+ }
288
+ });
289
+ ```
290
+
291
+ **Browser Voice Input (Web Speech API):**
292
+ ```typescript
293
+ // In browser environment
294
+ const SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
295
+ const recognition = new SpeechRecognition();
296
+
297
+ recognition.onresult = async (event) => {
298
+ const transcript = event.results[0][0].transcript;
299
+
300
+ // Process voice input with AI
301
+ const explain = wrap((text: string) => text, { task: 'explain' });
302
+ const result = await explain(transcript);
303
+ console.log(result.output);
304
+ };
305
+
306
+ recognition.start();
307
+ ```
308
+
309
+ **Multimodal Interface:**
310
+ ```typescript
311
+ interface MultimodalInput {
312
+ text?: string; // Text content
313
+ image?: string; // Base64 encoded image (with data URI)
314
+ file?: { // File attachment
315
+ name: string; // File name
316
+ data: string; // Base64 encoded (with data URI)
317
+ type: string; // MIME type
318
+ };
319
+ }
320
+ ```
321
+
322
+ **Use Cases:**
323
+ - 📸 Image analysis and description
324
+ - 📄 OCR and document processing
325
+ - 🔍 Code review from screenshots
326
+ - 🎤 Voice commands and transcription
327
+ - 📊 Chart and diagram analysis
328
+ - 🏷️ Product image tagging
329
+
330
+ **See full examples:**
331
+ - [Vision Examples](./examples/multimodal/vision-example.ts)
332
+ - [Voice Examples](./examples/multimodal/voice-example.ts)
333
+ - [React Demo with UI](./examples/react/)
334
+
335
+ ### 5. Error Handling
234
336
 
235
337
  ```typescript
236
338
  try {
@@ -336,7 +438,7 @@ const summarize = wrap((t: string) => t, {
336
438
  ### Debug Mode
337
439
 
338
440
  ```bash
339
- AI_HOOK_DEBUG=true
441
+ DEBUG=true
340
442
  ```
341
443
 
342
444
  Output:
@@ -354,7 +456,7 @@ Output:
354
456
  ### Old Way (v1.x)
355
457
  ```typescript
356
458
  // Set environment variables
357
- process.env.AI_HOOK_OPENAI_KEY = 'sk-...';
459
+ process.env.OPENAI_KEY = 'sk-...';
358
460
 
359
461
  // Use providers
360
462
  import { getProvider } from 'npm-ai-hooks';
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reset = exports.isInitialized = exports.getProvider = exports.getAvailableProviders = exports.removeProvider = exports.addProvider = exports.initAIHooks = exports.wrap = void 0;
3
+ exports.quickValidateKeyFormat = exports.validateApiKeys = exports.validateApiKey = exports.getAllProviderInfo = exports.getProviderInfo = exports.getAllProviders = exports.getProviderModels = exports.PROVIDER_NAMES = exports.reset = exports.isInitialized = exports.getProvider = exports.getAvailableProviders = exports.removeProvider = exports.addProvider = exports.initAIHooks = exports.wrap = void 0;
4
4
  // src/index.ts
5
5
  var wrap_1 = require("./wrap");
6
6
  Object.defineProperty(exports, "wrap", { enumerable: true, get: function () { return wrap_1.wrap; } });
@@ -12,3 +12,15 @@ Object.defineProperty(exports, "getAvailableProviders", { enumerable: true, get:
12
12
  Object.defineProperty(exports, "getProvider", { enumerable: true, get: function () { return providers_1.getProvider; } });
13
13
  Object.defineProperty(exports, "isInitialized", { enumerable: true, get: function () { return providers_1.isInitialized; } });
14
14
  Object.defineProperty(exports, "reset", { enumerable: true, get: function () { return providers_1.reset; } });
15
+ // Export provider utilities
16
+ var providerInfo_1 = require("./utils/providerInfo");
17
+ Object.defineProperty(exports, "PROVIDER_NAMES", { enumerable: true, get: function () { return providerInfo_1.PROVIDER_NAMES; } });
18
+ Object.defineProperty(exports, "getProviderModels", { enumerable: true, get: function () { return providerInfo_1.getProviderModels; } });
19
+ Object.defineProperty(exports, "getAllProviders", { enumerable: true, get: function () { return providerInfo_1.getAllProviders; } });
20
+ Object.defineProperty(exports, "getProviderInfo", { enumerable: true, get: function () { return providerInfo_1.getProviderInfo; } });
21
+ Object.defineProperty(exports, "getAllProviderInfo", { enumerable: true, get: function () { return providerInfo_1.getAllProviderInfo; } });
22
+ // Export key validation utilities
23
+ var keyValidator_1 = require("./utils/keyValidator");
24
+ Object.defineProperty(exports, "validateApiKey", { enumerable: true, get: function () { return keyValidator_1.validateApiKey; } });
25
+ Object.defineProperty(exports, "validateApiKeys", { enumerable: true, get: function () { return keyValidator_1.validateApiKeys; } });
26
+ Object.defineProperty(exports, "quickValidateKeyFormat", { enumerable: true, get: function () { return keyValidator_1.quickValidateKeyFormat; } });
@@ -29,7 +29,7 @@ exports.providerConfigs = {
29
29
  openai: {
30
30
  name: "openai",
31
31
  baseUrl: "https://api.openai.com/v1/chat/completions",
32
- envKey: "AI_HOOK_OPENAI_KEY",
32
+ envKey: "OPENAI_KEY",
33
33
  headers: { "Content-Type": "application/json" },
34
34
  requestBody: exports.requestBodyBuilders.openaiStyle,
35
35
  responseParser: exports.responseParsers.openaiStyle,
@@ -46,7 +46,7 @@ exports.providerConfigs = {
46
46
  groq: {
47
47
  name: "groq",
48
48
  baseUrl: "https://api.groq.com/openai/v1/chat/completions",
49
- envKey: "AI_HOOK_GROQ_KEY",
49
+ envKey: "GROQ_KEY",
50
50
  headers: { "Content-Type": "application/json" },
51
51
  requestBody: exports.requestBodyBuilders.openaiStyle,
52
52
  responseParser: exports.responseParsers.openaiStyle,
@@ -63,7 +63,7 @@ exports.providerConfigs = {
63
63
  claude: {
64
64
  name: "claude",
65
65
  baseUrl: "https://api.anthropic.com/v1/messages",
66
- envKey: "AI_HOOK_CLAUDE_KEY",
66
+ envKey: "CLAUDE_KEY",
67
67
  headers: {
68
68
  "Content-Type": "application/json",
69
69
  "anthropic-version": "2023-06-01"
@@ -83,7 +83,7 @@ exports.providerConfigs = {
83
83
  gemini: {
84
84
  name: "gemini",
85
85
  baseUrl: "https://generativelanguage.googleapis.com/v1beta/models",
86
- envKey: "AI_HOOK_GEMINI_KEY",
86
+ envKey: "GEMINI_KEY",
87
87
  headers: { "Content-Type": "application/json" },
88
88
  requestBody: exports.requestBodyBuilders.geminiStyle,
89
89
  responseParser: exports.responseParsers.geminiStyle,
@@ -100,7 +100,7 @@ exports.providerConfigs = {
100
100
  deepseek: {
101
101
  name: "deepseek",
102
102
  baseUrl: "https://api.deepseek.com/v1/chat/completions",
103
- envKey: "AI_HOOK_DEEPSEEK_KEY",
103
+ envKey: "DEEPSEEK_KEY",
104
104
  headers: { "Content-Type": "application/json" },
105
105
  requestBody: exports.requestBodyBuilders.openaiStyle,
106
106
  responseParser: exports.responseParsers.openaiStyle,
@@ -117,7 +117,7 @@ exports.providerConfigs = {
117
117
  mistral: {
118
118
  name: "mistral",
119
119
  baseUrl: "https://api.mistral.ai/v1/chat/completions",
120
- envKey: "AI_HOOK_MISTRAL_KEY",
120
+ envKey: "MISTRAL_KEY",
121
121
  headers: { "Content-Type": "application/json" },
122
122
  requestBody: exports.requestBodyBuilders.openaiStyle,
123
123
  responseParser: exports.responseParsers.openaiStyle,
@@ -134,7 +134,7 @@ exports.providerConfigs = {
134
134
  xai: {
135
135
  name: "xai",
136
136
  baseUrl: "https://api.x.ai/v1/chat/completions",
137
- envKey: "AI_HOOK_XAI_KEY",
137
+ envKey: "XAI_KEY",
138
138
  headers: { "Content-Type": "application/json" },
139
139
  requestBody: exports.requestBodyBuilders.openaiStyle,
140
140
  responseParser: exports.responseParsers.openaiStyle,
@@ -151,7 +151,7 @@ exports.providerConfigs = {
151
151
  perplexity: {
152
152
  name: "perplexity",
153
153
  baseUrl: "https://api.perplexity.ai/chat/completions",
154
- envKey: "AI_HOOK_PERPLEXITY_KEY",
154
+ envKey: "PERPLEXITY_KEY",
155
155
  headers: { "Content-Type": "application/json" },
156
156
  requestBody: exports.requestBodyBuilders.openaiStyle,
157
157
  responseParser: exports.responseParsers.openaiStyle,
@@ -168,7 +168,7 @@ exports.providerConfigs = {
168
168
  openrouter: {
169
169
  name: "openrouter",
170
170
  baseUrl: "https://openrouter.ai/api/v1/chat/completions",
171
- envKey: "AI_HOOK_OPENROUTER_KEY",
171
+ envKey: "OPENROUTER_KEY",
172
172
  headers: { "Content-Type": "application/json" },
173
173
  requestBody: exports.requestBodyBuilders.openaiStyle,
174
174
  responseParser: exports.responseParsers.openaiStyle,
@@ -78,5 +78,5 @@ function getProvider(name) {
78
78
  return { fn: providers[available[0]], provider: available[0] };
79
79
  }
80
80
  // 3. No valid keys found → throw error (single instruction, no fallback)
81
- throw new errors_1.AIHookError("NO_PROVIDER_FOUND", "No valid AI provider API key was found.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - AI_HOOK_OPENAI_KEY\n - AI_HOOK_OPENROUTER_KEY\n - AI_HOOK_GROQ_KEY\n", undefined, "Reference .env.example for setup instructions.");
81
+ throw new errors_1.AIHookError("NO_PROVIDER_FOUND", "No valid AI provider API key was found.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - OPENAI_KEY\n - OPENROUTER_KEY\n - GROQ_KEY\n", undefined, "Reference .env.example for setup instructions.");
82
82
  }
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateApiKey = validateApiKey;
4
+ exports.validateApiKeys = validateApiKeys;
5
+ exports.quickValidateKeyFormat = quickValidateKeyFormat;
6
+ const BaseProvider_1 = require("../providers/base/BaseProvider");
7
+ const SpecializedProviders_1 = require("../providers/base/SpecializedProviders");
8
+ const ProviderConfigs_1 = require("../providers/base/ProviderConfigs");
9
+ /**
10
+ * Validates an API key by making a minimal test request to the provider
11
+ */
12
+ async function validateApiKey(provider, apiKey) {
13
+ if (!apiKey || apiKey.trim() === '') {
14
+ return {
15
+ valid: false,
16
+ provider,
17
+ error: 'EMPTY_KEY',
18
+ message: 'API key is empty'
19
+ };
20
+ }
21
+ try {
22
+ let providerInstance;
23
+ // Create provider instance based on type
24
+ switch (provider) {
25
+ case 'claude':
26
+ providerInstance = new SpecializedProviders_1.ClaudeProvider();
27
+ break;
28
+ case 'gemini':
29
+ providerInstance = new SpecializedProviders_1.GeminiProvider();
30
+ break;
31
+ case 'openrouter':
32
+ providerInstance = new SpecializedProviders_1.OpenRouterProvider();
33
+ break;
34
+ default:
35
+ const config = ProviderConfigs_1.providerConfigs[provider];
36
+ if (!config) {
37
+ return {
38
+ valid: false,
39
+ provider,
40
+ error: 'UNKNOWN_PROVIDER',
41
+ message: `Unknown provider: ${provider}`
42
+ };
43
+ }
44
+ providerInstance = new BaseProvider_1.BaseProvider(config);
45
+ }
46
+ // Set the API key temporarily
47
+ providerInstance.apiKey = apiKey;
48
+ // Make a minimal test request
49
+ const testPrompt = "Hi";
50
+ const testModel = getDefaultModel(provider);
51
+ await providerInstance.call(testPrompt, testModel);
52
+ return {
53
+ valid: true,
54
+ provider,
55
+ message: 'API key is valid'
56
+ };
57
+ }
58
+ catch (error) {
59
+ // Check for common error types
60
+ if (error.message?.includes('401') || error.message?.includes('Unauthorized') || error.message?.includes('Invalid API key')) {
61
+ return {
62
+ valid: false,
63
+ provider,
64
+ error: 'INVALID_KEY',
65
+ message: 'API key is invalid or unauthorized'
66
+ };
67
+ }
68
+ if (error.message?.includes('403') || error.message?.includes('Forbidden')) {
69
+ return {
70
+ valid: false,
71
+ provider,
72
+ error: 'FORBIDDEN',
73
+ message: 'API key does not have access to this resource'
74
+ };
75
+ }
76
+ if (error.message?.includes('429') || error.message?.includes('rate limit')) {
77
+ // Rate limit means the key is valid, just too many requests
78
+ return {
79
+ valid: true,
80
+ provider,
81
+ message: 'API key is valid (rate limited)'
82
+ };
83
+ }
84
+ // If we get other errors, the key might still be valid (could be model/format issues)
85
+ return {
86
+ valid: true, // Assume valid unless we get clear auth error
87
+ provider,
88
+ message: 'API key appears valid (test request had non-auth error)'
89
+ };
90
+ }
91
+ }
92
+ /**
93
+ * Validates multiple API keys
94
+ */
95
+ async function validateApiKeys(keys) {
96
+ const results = {};
97
+ for (const [provider, key] of Object.entries(keys)) {
98
+ if (key && key.trim()) {
99
+ results[provider] = await validateApiKey(provider, key);
100
+ }
101
+ }
102
+ return results;
103
+ }
104
+ /**
105
+ * Get default model for a provider (for testing)
106
+ */
107
+ function getDefaultModel(provider) {
108
+ const defaultModels = {
109
+ openai: 'gpt-3.5-turbo',
110
+ claude: 'claude-3-haiku-20240307',
111
+ gemini: 'gemini-1.5-flash',
112
+ groq: 'llama-3.1-8b-instant',
113
+ deepseek: 'deepseek-chat',
114
+ xai: 'grok-beta',
115
+ mistral: 'mistral-small-latest',
116
+ perplexity: 'llama-3.1-sonar-small-128k-chat',
117
+ openrouter: 'openai/gpt-3.5-turbo'
118
+ };
119
+ return defaultModels[provider] || 'default';
120
+ }
121
+ /**
122
+ * Quick check if API key format looks valid (doesn't make API call)
123
+ */
124
+ function quickValidateKeyFormat(provider, apiKey) {
125
+ if (!apiKey || apiKey.trim() === '') {
126
+ return {
127
+ valid: false,
128
+ provider,
129
+ error: 'EMPTY_KEY',
130
+ message: 'API key is empty'
131
+ };
132
+ }
133
+ // Check key format patterns
134
+ const patterns = {
135
+ openai: /^sk-[a-zA-Z0-9-_]{20,}$/,
136
+ claude: /^sk-ant-[a-zA-Z0-9-_]{20,}$/,
137
+ gemini: /^AIza[a-zA-Z0-9-_]{35}$/,
138
+ groq: /^gsk_[a-zA-Z0-9]{52}$/,
139
+ deepseek: /^sk-[a-zA-Z0-9]{32}$/,
140
+ xai: /^xai-[a-zA-Z0-9-_]{40,}$/,
141
+ mistral: /^[a-zA-Z0-9]{32}$/,
142
+ perplexity: /^pplx-[a-zA-Z0-9]{40}$/,
143
+ openrouter: /^sk-or-v1-[a-zA-Z0-9]{64}$/
144
+ };
145
+ const pattern = patterns[provider];
146
+ if (pattern && !pattern.test(apiKey)) {
147
+ return {
148
+ valid: false,
149
+ provider,
150
+ error: 'INVALID_FORMAT',
151
+ message: `API key format doesn't match expected pattern for ${provider}`
152
+ };
153
+ }
154
+ return {
155
+ valid: true,
156
+ provider,
157
+ message: 'API key format looks valid'
158
+ };
159
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROVIDER_NAMES = void 0;
4
+ exports.getProviderModels = getProviderModels;
5
+ exports.getAllProviders = getAllProviders;
6
+ exports.getProviderInfo = getProviderInfo;
7
+ exports.getAllProviderInfo = getAllProviderInfo;
8
+ // Provider display names
9
+ exports.PROVIDER_NAMES = {
10
+ openai: "OpenAI",
11
+ claude: "Claude",
12
+ gemini: "Gemini",
13
+ groq: "Groq",
14
+ deepseek: "DeepSeek",
15
+ xai: "xAI",
16
+ mistral: "Mistral",
17
+ perplexity: "Perplexity",
18
+ openrouter: "OpenRouter"
19
+ };
20
+ // Get all available models for a provider
21
+ function getProviderModels(provider) {
22
+ // These are the actual model types from the library
23
+ const modelMap = {
24
+ openai: [
25
+ "gpt-4.1",
26
+ "gpt-4.1-mini",
27
+ "gpt-4o",
28
+ "gpt-4o-mini",
29
+ "gpt-4-turbo",
30
+ "gpt-4",
31
+ "gpt-3.5-turbo"
32
+ ],
33
+ claude: [
34
+ "claude-3-5-sonnet-20241022",
35
+ "claude-3-5-haiku-20241022",
36
+ "claude-3-opus-20240229",
37
+ "claude-3-sonnet-20240229",
38
+ "claude-3-haiku-20240307"
39
+ ],
40
+ gemini: [
41
+ "gemini-2.0-flash-exp",
42
+ "gemini-2.0-flash-thinking-exp-01-21",
43
+ "gemini-exp-1206",
44
+ "gemini-1.5-pro",
45
+ "gemini-1.5-flash",
46
+ "gemini-1.5-flash-8b"
47
+ ],
48
+ groq: [
49
+ "llama-3.3-70b-versatile",
50
+ "llama-3.1-8b-instant",
51
+ "gemma2-9b-it"
52
+ ],
53
+ deepseek: [
54
+ "deepseek-chat",
55
+ "deepseek-reasoner"
56
+ ],
57
+ xai: [
58
+ "grok-4-0131",
59
+ "grok-beta",
60
+ "grok-2-1212"
61
+ ],
62
+ mistral: [
63
+ "mistral-large-latest",
64
+ "mistral-small-latest",
65
+ "pixtral-large-latest",
66
+ "ministral-8b-latest",
67
+ "mistral-nemo"
68
+ ],
69
+ perplexity: [
70
+ "llama-3.1-sonar-large-128k-online",
71
+ "llama-3.1-sonar-small-128k-online",
72
+ "llama-3.1-sonar-large-128k-chat",
73
+ "llama-3.1-sonar-small-128k-chat"
74
+ ],
75
+ openrouter: [
76
+ "openai/gpt-4o",
77
+ "anthropic/claude-3.5-sonnet",
78
+ "google/gemini-2.0-flash-exp",
79
+ "meta-llama/llama-3.3-70b-instruct",
80
+ "deepseek/deepseek-chat"
81
+ ]
82
+ };
83
+ return modelMap[provider] || [];
84
+ }
85
+ // Get all supported providers
86
+ function getAllProviders() {
87
+ return Object.keys(exports.PROVIDER_NAMES);
88
+ }
89
+ function getProviderInfo(provider) {
90
+ return {
91
+ key: provider,
92
+ name: exports.PROVIDER_NAMES[provider],
93
+ models: getProviderModels(provider)
94
+ };
95
+ }
96
+ function getAllProviderInfo() {
97
+ return getAllProviders().map(getProviderInfo);
98
+ }
package/dist/cjs/wrap.js CHANGED
@@ -34,16 +34,43 @@ function wrap(fn, options) {
34
34
  }
35
35
  return async (...args) => {
36
36
  try {
37
- const input = fn(...args);
37
+ // Await the function result to handle both sync and async functions
38
+ const rawInput = fn(...args);
39
+ const input = rawInput instanceof Promise ? await rawInput : rawInput;
40
+ // Check if input is multimodal
41
+ const isMultimodal = typeof input === 'object' && input !== null && ('image' in input || 'file' in input);
42
+ const textInput = isMultimodal ? input.text || '' : String(input);
43
+ const imageData = isMultimodal ? input.image : undefined;
44
+ const fileData = isMultimodal ? input.file : undefined;
38
45
  // Step 1: get provider function and the actual provider name
39
46
  const { fn: providerFn, provider: providerKey } = (0, providers_1.getProvider)(options.provider);
40
47
  // Step 2: pick model: passed model or provider-specific default
41
48
  const model = options.model || (providerKey in types_1.DEFAULT_MODEL ? types_1.DEFAULT_MODEL[providerKey] : undefined);
42
49
  if (!model) {
43
- throw new errors_1.AIHookError("NO_MODEL_FOUND", "No model found: You must specify a provider or pass a valid model.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - AI_HOOK_OPENAI_KEY\n - AI_HOOK_OPENROUTER_KEY\n - AI_HOOK_GROQ_KEY\n", options.provider, "Reference .env.example for setup instructions.");
50
+ throw new errors_1.AIHookError("NO_MODEL_FOUND", "No model found: You must specify a provider or pass a valid model.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - OPENAI_KEY\n - OPENROUTER_KEY\n - GROQ_KEY\n", options.provider, "Reference .env.example for setup instructions.");
51
+ }
52
+ // Step 3: build prompt with multimodal support
53
+ let prompt;
54
+ if (options.customPrompt) {
55
+ // Use custom prompt if provided
56
+ prompt = `${options.customPrompt}\n\n${textInput}`;
57
+ if (imageData) {
58
+ prompt = `${prompt}\n\n[Image attached]`;
59
+ }
60
+ if (fileData) {
61
+ prompt = `${prompt}\n\n[File: ${fileData.name}]`;
62
+ }
63
+ }
64
+ else {
65
+ // Use built-in task prompt
66
+ prompt = buildPrompt(options.task, textInput, options.targetLanguage);
67
+ if (imageData) {
68
+ prompt = `${prompt}\n\n[Image attached]`;
69
+ }
70
+ if (fileData) {
71
+ prompt = `${prompt}\n\n[File: ${fileData.name}]`;
72
+ }
44
73
  }
45
- // Step 3: build prompt
46
- const prompt = buildPrompt(options.task, input, options.targetLanguage);
47
74
  const startTime = Date.now();
48
75
  let output;
49
76
  try {
package/dist/esm/index.js CHANGED
@@ -1,3 +1,7 @@
1
1
  // src/index.ts
2
2
  export { wrap } from "./wrap";
3
3
  export { initAIHooks, addProvider, removeProvider, getAvailableProviders, getProvider, isInitialized, reset } from "./providers";
4
+ // Export provider utilities
5
+ export { PROVIDER_NAMES, getProviderModels, getAllProviders, getProviderInfo, getAllProviderInfo } from "./utils/providerInfo";
6
+ // Export key validation utilities
7
+ export { validateApiKey, validateApiKeys, quickValidateKeyFormat } from "./utils/keyValidator";
@@ -26,7 +26,7 @@ export const providerConfigs = {
26
26
  openai: {
27
27
  name: "openai",
28
28
  baseUrl: "https://api.openai.com/v1/chat/completions",
29
- envKey: "AI_HOOK_OPENAI_KEY",
29
+ envKey: "OPENAI_KEY",
30
30
  headers: { "Content-Type": "application/json" },
31
31
  requestBody: requestBodyBuilders.openaiStyle,
32
32
  responseParser: responseParsers.openaiStyle,
@@ -43,7 +43,7 @@ export const providerConfigs = {
43
43
  groq: {
44
44
  name: "groq",
45
45
  baseUrl: "https://api.groq.com/openai/v1/chat/completions",
46
- envKey: "AI_HOOK_GROQ_KEY",
46
+ envKey: "GROQ_KEY",
47
47
  headers: { "Content-Type": "application/json" },
48
48
  requestBody: requestBodyBuilders.openaiStyle,
49
49
  responseParser: responseParsers.openaiStyle,
@@ -60,7 +60,7 @@ export const providerConfigs = {
60
60
  claude: {
61
61
  name: "claude",
62
62
  baseUrl: "https://api.anthropic.com/v1/messages",
63
- envKey: "AI_HOOK_CLAUDE_KEY",
63
+ envKey: "CLAUDE_KEY",
64
64
  headers: {
65
65
  "Content-Type": "application/json",
66
66
  "anthropic-version": "2023-06-01"
@@ -80,7 +80,7 @@ export const providerConfigs = {
80
80
  gemini: {
81
81
  name: "gemini",
82
82
  baseUrl: "https://generativelanguage.googleapis.com/v1beta/models",
83
- envKey: "AI_HOOK_GEMINI_KEY",
83
+ envKey: "GEMINI_KEY",
84
84
  headers: { "Content-Type": "application/json" },
85
85
  requestBody: requestBodyBuilders.geminiStyle,
86
86
  responseParser: responseParsers.geminiStyle,
@@ -97,7 +97,7 @@ export const providerConfigs = {
97
97
  deepseek: {
98
98
  name: "deepseek",
99
99
  baseUrl: "https://api.deepseek.com/v1/chat/completions",
100
- envKey: "AI_HOOK_DEEPSEEK_KEY",
100
+ envKey: "DEEPSEEK_KEY",
101
101
  headers: { "Content-Type": "application/json" },
102
102
  requestBody: requestBodyBuilders.openaiStyle,
103
103
  responseParser: responseParsers.openaiStyle,
@@ -114,7 +114,7 @@ export const providerConfigs = {
114
114
  mistral: {
115
115
  name: "mistral",
116
116
  baseUrl: "https://api.mistral.ai/v1/chat/completions",
117
- envKey: "AI_HOOK_MISTRAL_KEY",
117
+ envKey: "MISTRAL_KEY",
118
118
  headers: { "Content-Type": "application/json" },
119
119
  requestBody: requestBodyBuilders.openaiStyle,
120
120
  responseParser: responseParsers.openaiStyle,
@@ -131,7 +131,7 @@ export const providerConfigs = {
131
131
  xai: {
132
132
  name: "xai",
133
133
  baseUrl: "https://api.x.ai/v1/chat/completions",
134
- envKey: "AI_HOOK_XAI_KEY",
134
+ envKey: "XAI_KEY",
135
135
  headers: { "Content-Type": "application/json" },
136
136
  requestBody: requestBodyBuilders.openaiStyle,
137
137
  responseParser: responseParsers.openaiStyle,
@@ -148,7 +148,7 @@ export const providerConfigs = {
148
148
  perplexity: {
149
149
  name: "perplexity",
150
150
  baseUrl: "https://api.perplexity.ai/chat/completions",
151
- envKey: "AI_HOOK_PERPLEXITY_KEY",
151
+ envKey: "PERPLEXITY_KEY",
152
152
  headers: { "Content-Type": "application/json" },
153
153
  requestBody: requestBodyBuilders.openaiStyle,
154
154
  responseParser: responseParsers.openaiStyle,
@@ -165,7 +165,7 @@ export const providerConfigs = {
165
165
  openrouter: {
166
166
  name: "openrouter",
167
167
  baseUrl: "https://openrouter.ai/api/v1/chat/completions",
168
- envKey: "AI_HOOK_OPENROUTER_KEY",
168
+ envKey: "OPENROUTER_KEY",
169
169
  headers: { "Content-Type": "application/json" },
170
170
  requestBody: requestBodyBuilders.openaiStyle,
171
171
  responseParser: responseParsers.openaiStyle,
@@ -67,7 +67,7 @@ export function getProvider(name) {
67
67
  return { fn: providers[available[0]], provider: available[0] };
68
68
  }
69
69
  // 3. No valid keys found → throw error (single instruction, no fallback)
70
- throw new AIHookError("NO_PROVIDER_FOUND", "No valid AI provider API key was found.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - AI_HOOK_OPENAI_KEY\n - AI_HOOK_OPENROUTER_KEY\n - AI_HOOK_GROQ_KEY\n", undefined, "Reference .env.example for setup instructions.");
70
+ throw new AIHookError("NO_PROVIDER_FOUND", "No valid AI provider API key was found.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - OPENAI_KEY\n - OPENROUTER_KEY\n - GROQ_KEY\n", undefined, "Reference .env.example for setup instructions.");
71
71
  }
72
72
  // Export the new initialization system
73
73
  export { initAIHooks, addProvider, removeProvider, isInitialized, reset };
@@ -0,0 +1,154 @@
1
+ import { BaseProvider } from "../providers/base/BaseProvider";
2
+ import { ClaudeProvider, GeminiProvider, OpenRouterProvider } from "../providers/base/SpecializedProviders";
3
+ import { providerConfigs } from "../providers/base/ProviderConfigs";
4
+ /**
5
+ * Validates an API key by making a minimal test request to the provider
6
+ */
7
+ export async function validateApiKey(provider, apiKey) {
8
+ if (!apiKey || apiKey.trim() === '') {
9
+ return {
10
+ valid: false,
11
+ provider,
12
+ error: 'EMPTY_KEY',
13
+ message: 'API key is empty'
14
+ };
15
+ }
16
+ try {
17
+ let providerInstance;
18
+ // Create provider instance based on type
19
+ switch (provider) {
20
+ case 'claude':
21
+ providerInstance = new ClaudeProvider();
22
+ break;
23
+ case 'gemini':
24
+ providerInstance = new GeminiProvider();
25
+ break;
26
+ case 'openrouter':
27
+ providerInstance = new OpenRouterProvider();
28
+ break;
29
+ default:
30
+ const config = providerConfigs[provider];
31
+ if (!config) {
32
+ return {
33
+ valid: false,
34
+ provider,
35
+ error: 'UNKNOWN_PROVIDER',
36
+ message: `Unknown provider: ${provider}`
37
+ };
38
+ }
39
+ providerInstance = new BaseProvider(config);
40
+ }
41
+ // Set the API key temporarily
42
+ providerInstance.apiKey = apiKey;
43
+ // Make a minimal test request
44
+ const testPrompt = "Hi";
45
+ const testModel = getDefaultModel(provider);
46
+ await providerInstance.call(testPrompt, testModel);
47
+ return {
48
+ valid: true,
49
+ provider,
50
+ message: 'API key is valid'
51
+ };
52
+ }
53
+ catch (error) {
54
+ // Check for common error types
55
+ if (error.message?.includes('401') || error.message?.includes('Unauthorized') || error.message?.includes('Invalid API key')) {
56
+ return {
57
+ valid: false,
58
+ provider,
59
+ error: 'INVALID_KEY',
60
+ message: 'API key is invalid or unauthorized'
61
+ };
62
+ }
63
+ if (error.message?.includes('403') || error.message?.includes('Forbidden')) {
64
+ return {
65
+ valid: false,
66
+ provider,
67
+ error: 'FORBIDDEN',
68
+ message: 'API key does not have access to this resource'
69
+ };
70
+ }
71
+ if (error.message?.includes('429') || error.message?.includes('rate limit')) {
72
+ // Rate limit means the key is valid, just too many requests
73
+ return {
74
+ valid: true,
75
+ provider,
76
+ message: 'API key is valid (rate limited)'
77
+ };
78
+ }
79
+ // If we get other errors, the key might still be valid (could be model/format issues)
80
+ return {
81
+ valid: true, // Assume valid unless we get clear auth error
82
+ provider,
83
+ message: 'API key appears valid (test request had non-auth error)'
84
+ };
85
+ }
86
+ }
87
+ /**
88
+ * Validates multiple API keys
89
+ */
90
+ export async function validateApiKeys(keys) {
91
+ const results = {};
92
+ for (const [provider, key] of Object.entries(keys)) {
93
+ if (key && key.trim()) {
94
+ results[provider] = await validateApiKey(provider, key);
95
+ }
96
+ }
97
+ return results;
98
+ }
99
+ /**
100
+ * Get default model for a provider (for testing)
101
+ */
102
+ function getDefaultModel(provider) {
103
+ const defaultModels = {
104
+ openai: 'gpt-3.5-turbo',
105
+ claude: 'claude-3-haiku-20240307',
106
+ gemini: 'gemini-1.5-flash',
107
+ groq: 'llama-3.1-8b-instant',
108
+ deepseek: 'deepseek-chat',
109
+ xai: 'grok-beta',
110
+ mistral: 'mistral-small-latest',
111
+ perplexity: 'llama-3.1-sonar-small-128k-chat',
112
+ openrouter: 'openai/gpt-3.5-turbo'
113
+ };
114
+ return defaultModels[provider] || 'default';
115
+ }
116
+ /**
117
+ * Quick check if API key format looks valid (doesn't make API call)
118
+ */
119
+ export function quickValidateKeyFormat(provider, apiKey) {
120
+ if (!apiKey || apiKey.trim() === '') {
121
+ return {
122
+ valid: false,
123
+ provider,
124
+ error: 'EMPTY_KEY',
125
+ message: 'API key is empty'
126
+ };
127
+ }
128
+ // Check key format patterns
129
+ const patterns = {
130
+ openai: /^sk-[a-zA-Z0-9-_]{20,}$/,
131
+ claude: /^sk-ant-[a-zA-Z0-9-_]{20,}$/,
132
+ gemini: /^AIza[a-zA-Z0-9-_]{35}$/,
133
+ groq: /^gsk_[a-zA-Z0-9]{52}$/,
134
+ deepseek: /^sk-[a-zA-Z0-9]{32}$/,
135
+ xai: /^xai-[a-zA-Z0-9-_]{40,}$/,
136
+ mistral: /^[a-zA-Z0-9]{32}$/,
137
+ perplexity: /^pplx-[a-zA-Z0-9]{40}$/,
138
+ openrouter: /^sk-or-v1-[a-zA-Z0-9]{64}$/
139
+ };
140
+ const pattern = patterns[provider];
141
+ if (pattern && !pattern.test(apiKey)) {
142
+ return {
143
+ valid: false,
144
+ provider,
145
+ error: 'INVALID_FORMAT',
146
+ message: `API key format doesn't match expected pattern for ${provider}`
147
+ };
148
+ }
149
+ return {
150
+ valid: true,
151
+ provider,
152
+ message: 'API key format looks valid'
153
+ };
154
+ }
@@ -0,0 +1,91 @@
1
+ // Provider display names
2
+ export const PROVIDER_NAMES = {
3
+ openai: "OpenAI",
4
+ claude: "Claude",
5
+ gemini: "Gemini",
6
+ groq: "Groq",
7
+ deepseek: "DeepSeek",
8
+ xai: "xAI",
9
+ mistral: "Mistral",
10
+ perplexity: "Perplexity",
11
+ openrouter: "OpenRouter"
12
+ };
13
+ // Get all available models for a provider
14
+ export function getProviderModels(provider) {
15
+ // These are the actual model types from the library
16
+ const modelMap = {
17
+ openai: [
18
+ "gpt-4.1",
19
+ "gpt-4.1-mini",
20
+ "gpt-4o",
21
+ "gpt-4o-mini",
22
+ "gpt-4-turbo",
23
+ "gpt-4",
24
+ "gpt-3.5-turbo"
25
+ ],
26
+ claude: [
27
+ "claude-3-5-sonnet-20241022",
28
+ "claude-3-5-haiku-20241022",
29
+ "claude-3-opus-20240229",
30
+ "claude-3-sonnet-20240229",
31
+ "claude-3-haiku-20240307"
32
+ ],
33
+ gemini: [
34
+ "gemini-2.0-flash-exp",
35
+ "gemini-2.0-flash-thinking-exp-01-21",
36
+ "gemini-exp-1206",
37
+ "gemini-1.5-pro",
38
+ "gemini-1.5-flash",
39
+ "gemini-1.5-flash-8b"
40
+ ],
41
+ groq: [
42
+ "llama-3.3-70b-versatile",
43
+ "llama-3.1-8b-instant",
44
+ "gemma2-9b-it"
45
+ ],
46
+ deepseek: [
47
+ "deepseek-chat",
48
+ "deepseek-reasoner"
49
+ ],
50
+ xai: [
51
+ "grok-4-0131",
52
+ "grok-beta",
53
+ "grok-2-1212"
54
+ ],
55
+ mistral: [
56
+ "mistral-large-latest",
57
+ "mistral-small-latest",
58
+ "pixtral-large-latest",
59
+ "ministral-8b-latest",
60
+ "mistral-nemo"
61
+ ],
62
+ perplexity: [
63
+ "llama-3.1-sonar-large-128k-online",
64
+ "llama-3.1-sonar-small-128k-online",
65
+ "llama-3.1-sonar-large-128k-chat",
66
+ "llama-3.1-sonar-small-128k-chat"
67
+ ],
68
+ openrouter: [
69
+ "openai/gpt-4o",
70
+ "anthropic/claude-3.5-sonnet",
71
+ "google/gemini-2.0-flash-exp",
72
+ "meta-llama/llama-3.3-70b-instruct",
73
+ "deepseek/deepseek-chat"
74
+ ]
75
+ };
76
+ return modelMap[provider] || [];
77
+ }
78
+ // Get all supported providers
79
+ export function getAllProviders() {
80
+ return Object.keys(PROVIDER_NAMES);
81
+ }
82
+ export function getProviderInfo(provider) {
83
+ return {
84
+ key: provider,
85
+ name: PROVIDER_NAMES[provider],
86
+ models: getProviderModels(provider)
87
+ };
88
+ }
89
+ export function getAllProviderInfo() {
90
+ return getAllProviders().map(getProviderInfo);
91
+ }
package/dist/esm/wrap.js CHANGED
@@ -31,16 +31,43 @@ export function wrap(fn, options) {
31
31
  }
32
32
  return async (...args) => {
33
33
  try {
34
- const input = fn(...args);
34
+ // Await the function result to handle both sync and async functions
35
+ const rawInput = fn(...args);
36
+ const input = rawInput instanceof Promise ? await rawInput : rawInput;
37
+ // Check if input is multimodal
38
+ const isMultimodal = typeof input === 'object' && input !== null && ('image' in input || 'file' in input);
39
+ const textInput = isMultimodal ? input.text || '' : String(input);
40
+ const imageData = isMultimodal ? input.image : undefined;
41
+ const fileData = isMultimodal ? input.file : undefined;
35
42
  // Step 1: get provider function and the actual provider name
36
43
  const { fn: providerFn, provider: providerKey } = getProvider(options.provider);
37
44
  // Step 2: pick model: passed model or provider-specific default
38
45
  const model = options.model || (providerKey in DEFAULT_MODEL ? DEFAULT_MODEL[providerKey] : undefined);
39
46
  if (!model) {
40
- throw new AIHookError("NO_MODEL_FOUND", "No model found: You must specify a provider or pass a valid model.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - AI_HOOK_OPENAI_KEY\n - AI_HOOK_OPENROUTER_KEY\n - AI_HOOK_GROQ_KEY\n", options.provider, "Reference .env.example for setup instructions.");
47
+ throw new AIHookError("NO_MODEL_FOUND", "No model found: You must specify a provider or pass a valid model.\n\nAt least one provider API key is required in your .env file.\n\nPlease add one of the following to your .env (see .env.example for details):\n - OPENAI_KEY\n - OPENROUTER_KEY\n - GROQ_KEY\n", options.provider, "Reference .env.example for setup instructions.");
48
+ }
49
+ // Step 3: build prompt with multimodal support
50
+ let prompt;
51
+ if (options.customPrompt) {
52
+ // Use custom prompt if provided
53
+ prompt = `${options.customPrompt}\n\n${textInput}`;
54
+ if (imageData) {
55
+ prompt = `${prompt}\n\n[Image attached]`;
56
+ }
57
+ if (fileData) {
58
+ prompt = `${prompt}\n\n[File: ${fileData.name}]`;
59
+ }
60
+ }
61
+ else {
62
+ // Use built-in task prompt
63
+ prompt = buildPrompt(options.task, textInput, options.targetLanguage);
64
+ if (imageData) {
65
+ prompt = `${prompt}\n\n[Image attached]`;
66
+ }
67
+ if (fileData) {
68
+ prompt = `${prompt}\n\n[File: ${fileData.name}]`;
69
+ }
41
70
  }
42
- // Step 3: build prompt
43
- const prompt = buildPrompt(options.task, input, options.targetLanguage);
44
71
  const startTime = Date.now();
45
72
  let output;
46
73
  try {
package/dist/index.d.ts CHANGED
@@ -1,2 +1,7 @@
1
1
  export { wrap } from "./wrap";
2
+ export type { MultimodalInput } from "./wrap";
2
3
  export { initAIHooks, addProvider, removeProvider, getAvailableProviders, getProvider, isInitialized, reset } from "./providers";
4
+ export type { Provider, ProviderModels, TaskType } from "./types";
5
+ export type { OpenAIModel, ClaudeModel, GeminiModel, GroqModel, DeepSeekModel, XAIModel, MistralModel, PerplexityModel, OpenRouterModel } from "./types";
6
+ export { PROVIDER_NAMES, getProviderModels, getAllProviders, getProviderInfo, getAllProviderInfo, type ProviderInfo } from "./utils/providerInfo";
7
+ export { validateApiKey, validateApiKeys, quickValidateKeyFormat, type KeyValidationResult } from "./utils/keyValidator";
@@ -7,6 +7,15 @@ import { DeepSeekModel } from "./deepseek";
7
7
  import { XAIModel } from "./xai";
8
8
  import { PerplexityModel } from "./perplexity";
9
9
  import { MistralModel } from "./mistral";
10
+ export type { OpenAIModel } from "./openai";
11
+ export type { OpenRouterModel } from "./openrouter";
12
+ export type { GroqModel } from "./groq";
13
+ export type { GeminiModel } from "./gemini";
14
+ export type { ClaudeModel } from "./claude";
15
+ export type { DeepSeekModel } from "./deepseek";
16
+ export type { XAIModel } from "./xai";
17
+ export type { PerplexityModel } from "./perplexity";
18
+ export type { MistralModel } from "./mistral";
10
19
  export type Provider = "openrouter" | "groq" | "openai" | "gemini" | "claude" | "deepseek" | "xai" | "perplexity" | "mistral";
11
20
  export type ProviderModels = {
12
21
  openrouter: OpenRouterModel;
@@ -26,11 +35,13 @@ export type TaskType = "summarize" | "translate" | "explain" | "rewrite" | "sent
26
35
  export type WrapOptions<P extends Provider | undefined = undefined> = {
27
36
  provider: P extends Provider ? P : never;
28
37
  model?: P extends Provider ? ProviderModels[P] : never;
29
- task: TaskType;
38
+ task?: TaskType;
30
39
  targetLanguage?: string;
40
+ customPrompt?: string;
31
41
  } | {
32
42
  provider?: never;
33
43
  model?: never;
34
- task: TaskType;
44
+ task?: TaskType;
35
45
  targetLanguage?: string;
46
+ customPrompt?: string;
36
47
  };
@@ -0,0 +1,19 @@
1
+ import type { Provider } from "../types";
2
+ export interface KeyValidationResult {
3
+ valid: boolean;
4
+ provider: Provider;
5
+ error?: string;
6
+ message: string;
7
+ }
8
+ /**
9
+ * Validates an API key by making a minimal test request to the provider
10
+ */
11
+ export declare function validateApiKey(provider: Provider, apiKey: string): Promise<KeyValidationResult>;
12
+ /**
13
+ * Validates multiple API keys
14
+ */
15
+ export declare function validateApiKeys(keys: Record<Provider, string>): Promise<Record<Provider, KeyValidationResult>>;
16
+ /**
17
+ * Quick check if API key format looks valid (doesn't make API call)
18
+ */
19
+ export declare function quickValidateKeyFormat(provider: Provider, apiKey: string): KeyValidationResult;
@@ -0,0 +1,11 @@
1
+ import type { Provider } from "../types";
2
+ export declare const PROVIDER_NAMES: Record<Provider, string>;
3
+ export declare function getProviderModels(provider: Provider): string[];
4
+ export declare function getAllProviders(): Provider[];
5
+ export interface ProviderInfo {
6
+ key: Provider;
7
+ name: string;
8
+ models: string[];
9
+ }
10
+ export declare function getProviderInfo(provider: Provider): ProviderInfo;
11
+ export declare function getAllProviderInfo(): ProviderInfo[];
package/dist/wrap.d.ts CHANGED
@@ -1,4 +1,13 @@
1
1
  import { WrapOptions, Provider } from "./types";
2
+ export interface MultimodalInput {
3
+ text?: string;
4
+ image?: string;
5
+ file?: {
6
+ name: string;
7
+ data: string;
8
+ type: string;
9
+ };
10
+ }
2
11
  export declare function wrap<T extends (...args: any[]) => any, P extends Provider | undefined = undefined>(fn: T, options: WrapOptions<P>): (...args: Parameters<T>) => Promise<{
3
12
  output: string;
4
13
  meta: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-ai-hooks",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Universal AI Hook Layer for Node.js and React – one wrapper for all AI providers. Inject LLM-like behavior into any JavaScript or TypeScript function with a single line, without writing prompts, handling SDKs, or locking into any provider.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -42,19 +42,6 @@
42
42
  "setup:dev:win": "scripts\\setup-dev.bat",
43
43
  "setup:dev:ps": "powershell -ExecutionPolicy Bypass -File scripts\\setup-dev.ps1"
44
44
  },
45
- "keywords": [
46
- "ai",
47
- "hooks",
48
- "llm",
49
- "nodejs",
50
- "typescript",
51
- "openai",
52
- "gemini",
53
- "claude",
54
- "deepseek",
55
- "groq",
56
- "mistral"
57
- ],
58
45
  "author": "AteebNoOne <ateebnoone@gmail.com>",
59
46
  "repository": {
60
47
  "type": "git",