@jz92/ai-provider 0.2.2 → 0.3.2

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
@@ -1,4 +1,4 @@
1
- # @jz92/ai-provider
1
+ # @jithin/ai-provider
2
2
 
3
3
  A zero-config AI routing layer for Node.js and Next.js projects.
4
4
 
@@ -52,7 +52,7 @@ For production (Vercel, AWS, etc.) you only need an API key from your chosen pro
52
52
  ## Installation
53
53
 
54
54
  ```bash
55
- npm install @jz92/ai-provider
55
+ npm install @jithin/ai-provider
56
56
  ```
57
57
 
58
58
  After install, a setup guide prints automatically telling you exactly which peer deps to install based on the providers you want to use. The short version:
@@ -81,18 +81,18 @@ npm install @ai-sdk/mistral # → MISTRAL_API_KEY
81
81
  ## Usage
82
82
 
83
83
  ```typescript
84
- import { generateStructured, generatePlainText } from '@jz92/ai-provider'
84
+ import { generateStructured, generatePlainText } from '@jithin/ai-provider'
85
85
  import { z } from 'zod'
86
86
 
87
87
  // Structured output — returns validated, typed JSON
88
88
  const result = await generateStructured({
89
89
  systemPrompt: 'Extract data. Respond in JSON only.',
90
- prompt: 'My name is Jithin and I live in Maidenhead.',
90
+ prompt: 'Find all orders placed in the last 30 days with status delivered.',
91
91
  schema: z.object({ name: z.string(), city: z.string() }),
92
92
  cacheKey: `extract:${input}`, // optional — skips API on repeat calls
93
93
  })
94
94
 
95
- console.log(result.data) // { name: 'Jithin', city: 'Maidenhead' }
95
+ console.log(result.data) // { collection: 'orders', operation: 'find', query: { ... } }
96
96
  console.log(result.provider) // 'ollama' locally · 'anthropic' in prod
97
97
  console.log(result.fromCache) // true on cache hit
98
98
 
@@ -263,7 +263,7 @@ ollama create my-feature -f modelfiles/Modelfile.my-feature
263
263
  OLLAMA_MODEL=my-feature
264
264
  ```
265
265
 
266
- A `Modelfile.template` is included at `node_modules/@jz92/ai-provider/modelfiles-template/Modelfile.template`.
266
+ A `Modelfile.template` is included at `node_modules/@jithin/ai-provider/modelfiles-template/Modelfile.template`.
267
267
 
268
268
  ---
269
269
 
@@ -286,7 +286,7 @@ Your responsibilities as a consumer:
286
286
  The package throws `AIProviderError` with a typed `code` and a clear actionable message. You never see raw SDK errors.
287
287
 
288
288
  ```typescript
289
- import { generateStructured, AIProviderError } from '@jz92/ai-provider'
289
+ import { generateStructured, AIProviderError } from '@jithin/ai-provider'
290
290
 
291
291
  try {
292
292
  const result = await generateStructured({ ... })
package/dist/index.cjs CHANGED
@@ -417,12 +417,17 @@ async function generateStructured(options) {
417
417
  const timeout = TIMEOUT_MS[config.provider];
418
418
  const result = await withRetry(
419
419
  () => withTimeout(
420
- () => (0, import_ai.generateObject)({ model, messages, schema: options.schema, maxTokens: config.maxTokens }),
420
+ () => (0, import_ai.generateText)({
421
+ model,
422
+ messages,
423
+ output: import_ai.Output.object({ schema: options.schema }),
424
+ maxOutputTokens: config.maxTokens
425
+ }),
421
426
  timeout
422
427
  ),
423
428
  config.provider
424
429
  );
425
- return buildResponse(result.object, result.usage, config, options.cacheKey);
430
+ return buildResponse(result.output, result.usage, config, options.cacheKey);
426
431
  }
427
432
  async function generatePlainText(options) {
428
433
  const config = resolveProvider();
@@ -436,7 +441,7 @@ async function generatePlainText(options) {
436
441
  const timeout = TIMEOUT_MS[config.provider];
437
442
  const result = await withRetry(
438
443
  () => withTimeout(
439
- () => (0, import_ai.generateText)({ model, messages, maxTokens: config.maxTokens }),
444
+ () => (0, import_ai.generateText)({ model, messages, maxOutputTokens: config.maxTokens }),
440
445
  timeout
441
446
  ),
442
447
  config.provider
@@ -453,7 +458,7 @@ function buildMessages(config, systemPrompt, userPrompt) {
453
458
  {
454
459
  type: "text",
455
460
  text: userPrompt,
456
- experimental_providerMetadata: {
461
+ providerOptions: {
457
462
  anthropic: { cacheControl: { type: "ephemeral" } }
458
463
  }
459
464
  }
@@ -468,17 +473,12 @@ function buildMessages(config, systemPrompt, userPrompt) {
468
473
  }
469
474
  function buildResponse(data, usage, config, cacheKey) {
470
475
  if (cacheKey) responseCache.set(cacheKey, data);
471
- let cachedTokens = 0;
472
- if (usage?.experimental_providerMetadata) {
473
- const meta = usage.experimental_providerMetadata;
474
- const anthropic = meta?.anthropic;
475
- cachedTokens = (anthropic?.cacheReadInputTokens ?? 0) + (anthropic?.cacheCreationInputTokens ?? 0);
476
- }
476
+ const cachedTokens = (usage?.inputTokenDetails?.cacheReadTokens ?? 0) + (usage?.inputTokenDetails?.cacheCreationTokens ?? 0);
477
477
  const response = {
478
478
  data,
479
479
  usage: usage ? {
480
- inputTokens: usage.promptTokens,
481
- outputTokens: usage.completionTokens,
480
+ inputTokens: usage.inputTokens ?? 0,
481
+ outputTokens: usage.outputTokens ?? 0,
482
482
  cachedTokens
483
483
  } : void 0,
484
484
  provider: config.provider,
@@ -506,8 +506,7 @@ function logUsage(config, response) {
506
506
  if (response.fromCache) {
507
507
  lines.push(line("tokens", "\x1B[2mskipped \u2014 response cache hit\x1B[0m"));
508
508
  } else if (u) {
509
- const tokenStr = `in: \x1B[33m${u.inputTokens}\x1B[0m out: \x1B[33m${u.outputTokens}\x1B[0m`;
510
- lines.push(line("tokens", tokenStr));
509
+ lines.push(line("tokens", `in: \x1B[33m${u.inputTokens}\x1B[0m out: \x1B[33m${u.outputTokens}\x1B[0m`));
511
510
  if (u.cachedTokens > 0) {
512
511
  const pct = Math.round(u.cachedTokens / u.inputTokens * 100);
513
512
  lines.push(line("cached", `\x1B[32m${u.cachedTokens} tokens (${pct}% of input)\x1B[0m`));
package/dist/index.d.cts CHANGED
@@ -41,6 +41,9 @@ type AIResponse<T> = {
41
41
  /**
42
42
  * generateStructured — typed, validated JSON output.
43
43
  *
44
+ * Uses generateText + Output.object() (ai@6+ pattern).
45
+ * generateObject is deprecated in ai@6 and removed in ai@7.
46
+ *
44
47
  * @example
45
48
  * const result = await generateStructured({
46
49
  * systemPrompt: MY_SYSTEM_PROMPT,
package/dist/index.d.ts CHANGED
@@ -41,6 +41,9 @@ type AIResponse<T> = {
41
41
  /**
42
42
  * generateStructured — typed, validated JSON output.
43
43
  *
44
+ * Uses generateText + Output.object() (ai@6+ pattern).
45
+ * generateObject is deprecated in ai@6 and removed in ai@7.
46
+ *
44
47
  * @example
45
48
  * const result = await generateStructured({
46
49
  * systemPrompt: MY_SYSTEM_PROMPT,
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/gateway.ts
2
- import { generateObject, generateText } from "ai";
2
+ import { generateText, Output } from "ai";
3
3
 
4
4
  // src/provider.ts
5
5
  function resolveProvider() {
@@ -377,12 +377,17 @@ async function generateStructured(options) {
377
377
  const timeout = TIMEOUT_MS[config.provider];
378
378
  const result = await withRetry(
379
379
  () => withTimeout(
380
- () => generateObject({ model, messages, schema: options.schema, maxTokens: config.maxTokens }),
380
+ () => generateText({
381
+ model,
382
+ messages,
383
+ output: Output.object({ schema: options.schema }),
384
+ maxOutputTokens: config.maxTokens
385
+ }),
381
386
  timeout
382
387
  ),
383
388
  config.provider
384
389
  );
385
- return buildResponse(result.object, result.usage, config, options.cacheKey);
390
+ return buildResponse(result.output, result.usage, config, options.cacheKey);
386
391
  }
387
392
  async function generatePlainText(options) {
388
393
  const config = resolveProvider();
@@ -396,7 +401,7 @@ async function generatePlainText(options) {
396
401
  const timeout = TIMEOUT_MS[config.provider];
397
402
  const result = await withRetry(
398
403
  () => withTimeout(
399
- () => generateText({ model, messages, maxTokens: config.maxTokens }),
404
+ () => generateText({ model, messages, maxOutputTokens: config.maxTokens }),
400
405
  timeout
401
406
  ),
402
407
  config.provider
@@ -413,7 +418,7 @@ function buildMessages(config, systemPrompt, userPrompt) {
413
418
  {
414
419
  type: "text",
415
420
  text: userPrompt,
416
- experimental_providerMetadata: {
421
+ providerOptions: {
417
422
  anthropic: { cacheControl: { type: "ephemeral" } }
418
423
  }
419
424
  }
@@ -428,17 +433,12 @@ function buildMessages(config, systemPrompt, userPrompt) {
428
433
  }
429
434
  function buildResponse(data, usage, config, cacheKey) {
430
435
  if (cacheKey) responseCache.set(cacheKey, data);
431
- let cachedTokens = 0;
432
- if (usage?.experimental_providerMetadata) {
433
- const meta = usage.experimental_providerMetadata;
434
- const anthropic = meta?.anthropic;
435
- cachedTokens = (anthropic?.cacheReadInputTokens ?? 0) + (anthropic?.cacheCreationInputTokens ?? 0);
436
- }
436
+ const cachedTokens = (usage?.inputTokenDetails?.cacheReadTokens ?? 0) + (usage?.inputTokenDetails?.cacheCreationTokens ?? 0);
437
437
  const response = {
438
438
  data,
439
439
  usage: usage ? {
440
- inputTokens: usage.promptTokens,
441
- outputTokens: usage.completionTokens,
440
+ inputTokens: usage.inputTokens ?? 0,
441
+ outputTokens: usage.outputTokens ?? 0,
442
442
  cachedTokens
443
443
  } : void 0,
444
444
  provider: config.provider,
@@ -466,8 +466,7 @@ function logUsage(config, response) {
466
466
  if (response.fromCache) {
467
467
  lines.push(line("tokens", "\x1B[2mskipped \u2014 response cache hit\x1B[0m"));
468
468
  } else if (u) {
469
- const tokenStr = `in: \x1B[33m${u.inputTokens}\x1B[0m out: \x1B[33m${u.outputTokens}\x1B[0m`;
470
- lines.push(line("tokens", tokenStr));
469
+ lines.push(line("tokens", `in: \x1B[33m${u.inputTokens}\x1B[0m out: \x1B[33m${u.outputTokens}\x1B[0m`));
471
470
  if (u.cachedTokens > 0) {
472
471
  const pct = Math.round(u.cachedTokens / u.inputTokens * 100);
473
472
  lines.push(line("cached", `\x1B[32m${u.cachedTokens} tokens (${pct}% of input)\x1B[0m`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jz92/ai-provider",
3
- "version": "0.2.2",
3
+ "version": "0.3.2",
4
4
  "description": "Environment-aware AI provider for portfolio projects. Local dev → Ollama, deployed → any cloud provider.",
5
5
  "author": "jz92",
6
6
  "license": "MIT",
@@ -31,14 +31,14 @@
31
31
  "prepublishOnly": "npm run build && npm run type-check"
32
32
  },
33
33
  "peerDependencies": {
34
- "ai": ">=4.0.0",
34
+ "ai": ">=6.0.0",
35
35
  "zod": ">=3.0.0",
36
- "@ai-sdk/anthropic": ">=1.0.0",
37
- "@ai-sdk/openai": ">=1.0.0",
38
- "@ai-sdk/google": ">=1.0.0",
39
- "@ai-sdk/groq": ">=1.0.0",
40
- "@ai-sdk/mistral": ">=1.0.0",
41
- "ollama-ai-provider": ">=0.16.0"
36
+ "@ai-sdk/anthropic": ">=3.0.0",
37
+ "@ai-sdk/openai": ">=3.0.0",
38
+ "@ai-sdk/google": ">=3.0.0",
39
+ "@ai-sdk/groq": ">=3.0.0",
40
+ "@ai-sdk/mistral": ">=3.0.0",
41
+ "ollama-ai-provider": ">=1.0.0"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "@ai-sdk/anthropic": {
@@ -1,33 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // Runs after npm install — prints a clear setup guide
4
- // so consumers know exactly which peer deps to install.
5
-
6
3
  const reset = '\x1b[0m'
7
4
  const bold = '\x1b[1m'
8
5
  const cyan = '\x1b[36m'
9
6
  const yellow = '\x1b[33m'
10
7
  const green = '\x1b[32m'
11
8
  const dim = '\x1b[2m'
9
+ const red = '\x1b[31m'
12
10
 
13
11
  console.log(`
14
12
  ${bold}@jz92/ai-provider${reset} installed successfully.
15
13
 
16
14
  ${yellow}Next: install the provider adapters you need.${reset}
17
15
 
18
- ${bold}Local dev (Ollama — free, no API key):${reset}
19
- ${cyan}npm install ollama-ai-provider${reset}
20
- ${dim}Then: brew install ollama && ollama pull qwen2.5-coder:14b${reset}
16
+ ${bold}Always required:${reset}
17
+ ${cyan}npm install ai zod${reset}
21
18
 
22
- ${bold}Cloud providers (install only what you use):${reset}
19
+ ${bold}Cloud providers install only what you use:${reset}
23
20
  ${cyan}npm install @ai-sdk/anthropic${reset} ${dim}→ ANTHROPIC_API_KEY https://console.anthropic.com${reset}
24
21
  ${cyan}npm install @ai-sdk/openai${reset} ${dim}→ OPENAI_API_KEY https://platform.openai.com/api-keys${reset}
25
22
  ${cyan}npm install @ai-sdk/google${reset} ${dim}→ GOOGLE_GENERATIVE_AI_API_KEY https://aistudio.google.com${reset}
26
23
  ${cyan}npm install @ai-sdk/groq${reset} ${dim}→ GROQ_API_KEY https://console.groq.com/keys${reset}
27
24
  ${cyan}npm install @ai-sdk/mistral${reset} ${dim}→ MISTRAL_API_KEY https://console.mistral.ai${reset}
28
25
 
29
- ${bold}Required in all cases:${reset}
30
- ${cyan}npm install ai zod${reset}
26
+ ${bold}Local dev with Ollama (devDependency — not for production):${reset}
27
+ ${cyan}npm install ollama-ai-provider --save-dev --legacy-peer-deps${reset}
28
+ ${dim}Then: brew install ollama && ollama pull qwen2.5-coder:14b${reset}
29
+
30
+ ${red}Note:${reset} ollama-ai-provider@1.2.0 conflicts with zod@4.
31
+ ${dim}--legacy-peer-deps is required until ollama-ai-provider updates.${reset}
32
+ ${dim}This does not affect production builds — devDependencies are excluded on Vercel/AWS.${reset}
31
33
 
32
34
  ${bold}Set NODE_ENV in your .env:${reset}
33
35
  ${dim}development${reset} → Ollama (free, local)