@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 +7 -7
- package/dist/index.cjs +13 -14
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +14 -15
- package/package.json +8 -8
- package/scripts/postinstall.js +11 -9
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
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 @
|
|
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 '@
|
|
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: '
|
|
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) // {
|
|
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/@
|
|
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 '@
|
|
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.
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
481
|
-
outputTokens: usage.
|
|
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
|
-
|
|
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 {
|
|
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
|
-
() =>
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
441
|
-
outputTokens: usage.
|
|
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
|
-
|
|
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.
|
|
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": ">=
|
|
34
|
+
"ai": ">=6.0.0",
|
|
35
35
|
"zod": ">=3.0.0",
|
|
36
|
-
"@ai-sdk/anthropic": ">=
|
|
37
|
-
"@ai-sdk/openai": ">=
|
|
38
|
-
"@ai-sdk/google": ">=
|
|
39
|
-
"@ai-sdk/groq": ">=
|
|
40
|
-
"@ai-sdk/mistral": ">=
|
|
41
|
-
"ollama-ai-provider": ">=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": {
|
package/scripts/postinstall.js
CHANGED
|
@@ -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}
|
|
19
|
-
${cyan}npm install
|
|
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
|
|
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}
|
|
30
|
-
${cyan}npm install ai
|
|
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)
|