@substrat-run/model-providers 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +31 -0
- package/dist/catalog.d.ts +65 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +64 -0
- package/dist/catalog.js.map +1 -0
- package/dist/host.d.ts +21 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +37 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/list-models.d.ts +12 -0
- package/dist/list-models.d.ts.map +1 -0
- package/dist/list-models.js +57 -0
- package/dist/list-models.js.map +1 -0
- package/dist/model-pairs.d.ts +38 -0
- package/dist/model-pairs.d.ts.map +1 -0
- package/dist/model-pairs.js +56 -0
- package/dist/model-pairs.js.map +1 -0
- package/dist/pricing.d.ts +52 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +100 -0
- package/dist/pricing.js.map +1 -0
- package/dist/provider-errors.d.ts +27 -0
- package/dist/provider-errors.d.ts.map +1 -0
- package/dist/provider-errors.js +74 -0
- package/dist/provider-errors.js.map +1 -0
- package/dist/providers.d.ts +116 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +193 -0
- package/dist/providers.js.map +1 -0
- package/dist/qwen-cache.d.ts +42 -0
- package/dist/qwen-cache.d.ts.map +1 -0
- package/dist/qwen-cache.js +118 -0
- package/dist/qwen-cache.js.map +1 -0
- package/dist/rate-card.generated.d.ts +16 -0
- package/dist/rate-card.generated.d.ts.map +1 -0
- package/dist/rate-card.generated.js +80 -0
- package/dist/rate-card.generated.js.map +1 -0
- package/dist/request.d.ts +10 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +44 -0
- package/dist/request.js.map +1 -0
- package/dist/resolve.d.ts +62 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +121 -0
- package/dist/resolve.js.map +1 -0
- package/dist/spec.d.ts +21 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +27 -0
- package/dist/spec.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit context-cache markers for the qwen dialect (H4 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* DashScope's cache is per-model: qwen3.8-max caches implicitly, but the flash
|
|
5
|
+
* tier caches ONLY when the request carries explicit Anthropic-style
|
|
6
|
+
* `cache_control: {type:'ephemeral'}` markers on CONTENT BLOCKS — verified
|
|
7
|
+
* against the token-plan gateway 2026-08-15: block-level markers cache ~99% of
|
|
8
|
+
* the prefix (creation billed at 125%, hits at 10%, 5-minute TTL, ≥1024-token
|
|
9
|
+
* blocks, max 4 markers/request); message-level markers are silently ignored,
|
|
10
|
+
* and `@ai-sdk/openai-compatible` can only emit message-level ones. So the
|
|
11
|
+
* markers are injected here, at the wire: a fetch wrapper rewrites each
|
|
12
|
+
* chat/completions body just-in-time.
|
|
13
|
+
*
|
|
14
|
+
* Placement is the same strategy `withMovingBreakpoint` runs for Claude —
|
|
15
|
+
* system prefix + the request's last message — and because the rewrite is
|
|
16
|
+
* stateless per request, the "moving" part comes free: each step marks its own
|
|
17
|
+
* tail, and the previous step's mark simply never existed on the wire again.
|
|
18
|
+
* Explicit markers are sent to ALL qwen models (max included): explicit read
|
|
19
|
+
* price (10%) undercuts the implicit discount, and the behavior is
|
|
20
|
+
* deterministic instead of best-effort.
|
|
21
|
+
*
|
|
22
|
+
* Worker-safe on purpose: both provider hosts (providers.ts, the node CLI, and
|
|
23
|
+
* providers-worker.ts, the hosted agent) wire this into `createOpenAICompatible`.
|
|
24
|
+
*/
|
|
25
|
+
const CACHE_CONTROL = { type: 'ephemeral' };
|
|
26
|
+
/** DashScope allows 4 markers per request; one is reserved for the tail. */
|
|
27
|
+
const MAX_SYSTEM_MARKERS = 3;
|
|
28
|
+
/** Marks a message's content in block form. Returns null when it cannot carry
|
|
29
|
+
* a marker (no content — e.g. an assistant message that is only tool_calls). */
|
|
30
|
+
function markMessage(m) {
|
|
31
|
+
if (typeof m.content === 'string') {
|
|
32
|
+
// DashScope rejects empty text blocks; an empty string cannot cache anyway.
|
|
33
|
+
if (m.content === '')
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
...m,
|
|
37
|
+
content: [{ type: 'text', text: m.content, cache_control: CACHE_CONTROL }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(m.content) && m.content.length > 0) {
|
|
41
|
+
const blocks = m.content;
|
|
42
|
+
const last = blocks[blocks.length - 1];
|
|
43
|
+
if (typeof last !== 'object' || last === null)
|
|
44
|
+
return null;
|
|
45
|
+
return {
|
|
46
|
+
...m,
|
|
47
|
+
content: [...blocks.slice(0, -1), { ...last, cache_control: CACHE_CONTROL }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The body transform: marker on each system message (the cross-turn-stable
|
|
54
|
+
* prefix — the only part reusable between turns) and on the last markable
|
|
55
|
+
* message (the within-turn moving breakpoint — each tool-loop step re-reads
|
|
56
|
+
* everything before its own tail from cache). Non-chat bodies pass through
|
|
57
|
+
* untouched.
|
|
58
|
+
*/
|
|
59
|
+
export function withQwenCacheMarkers(body) {
|
|
60
|
+
const b = body;
|
|
61
|
+
if (b === null || typeof b !== 'object' || !Array.isArray(b.messages))
|
|
62
|
+
return body;
|
|
63
|
+
const messages = b.messages.slice();
|
|
64
|
+
// The moving tail marker first — it is the one that compounds. Walk back
|
|
65
|
+
// past messages that cannot carry a block (tool_calls-only assistants).
|
|
66
|
+
let tail = -1;
|
|
67
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
68
|
+
const msg = messages[i];
|
|
69
|
+
if (msg.role === 'system')
|
|
70
|
+
break; // nothing but prefix left — its own markers below
|
|
71
|
+
const marked = markMessage(msg);
|
|
72
|
+
if (marked) {
|
|
73
|
+
messages[i] = marked;
|
|
74
|
+
tail = i;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let budget = MAX_SYSTEM_MARKERS;
|
|
79
|
+
for (let i = 0; i < messages.length && budget > 0; i++) {
|
|
80
|
+
const msg = messages[i];
|
|
81
|
+
if (msg.role !== 'system' || i === tail)
|
|
82
|
+
continue;
|
|
83
|
+
const marked = markMessage(msg);
|
|
84
|
+
if (marked) {
|
|
85
|
+
messages[i] = marked;
|
|
86
|
+
budget -= 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { ...body, messages };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Fetch wrapper for the qwen provider: rewrites POST chat/completions bodies
|
|
93
|
+
* through `withQwenCacheMarkers`. Anything unexpected — non-chat URL, a
|
|
94
|
+
* Request-object body, unparseable JSON — passes through unchanged: a request
|
|
95
|
+
* without markers is merely uncached, never broken.
|
|
96
|
+
*/
|
|
97
|
+
export function qwenCacheFetch(inner) {
|
|
98
|
+
const base = inner ?? ((...args) => globalThis.fetch(...args));
|
|
99
|
+
// Typed via the fetch signature itself — this file compiles under both the
|
|
100
|
+
// node and workers tsconfigs, which disagree on the DOM lib types.
|
|
101
|
+
const wrapped = async (input, init) => {
|
|
102
|
+
const url = typeof input === 'string'
|
|
103
|
+
? input
|
|
104
|
+
: (input.url ?? String(input));
|
|
105
|
+
if (!url.includes('/chat/completions') || typeof init?.body !== 'string') {
|
|
106
|
+
return base(input, init);
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const body = JSON.stringify(withQwenCacheMarkers(JSON.parse(init.body)));
|
|
110
|
+
return base(input, { ...init, body });
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return base(input, init);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
return wrapped;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=qwen-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qwen-cache.js","sourceRoot":"","sources":["../src/qwen-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED by scripts/update-rate-card.mjs — DO NOT EDIT.
|
|
3
|
+
*
|
|
4
|
+
* Regenerate: pnpm --filter @substrat-run/model-providers update-rate-card
|
|
5
|
+
* (review the diff — this file is the billing checkpoint, docs/architecture/builder/harness.md §2.2).
|
|
6
|
+
*
|
|
7
|
+
* Sources cross-checked at generation: models.dev api.json × LiteLLM
|
|
8
|
+
* model_prices_and_context_window.json (both MIT). Rates are provider LIST
|
|
9
|
+
* prices in USD per 1M tokens, decimal strings. Tier selection is
|
|
10
|
+
* all-or-nothing by the request's total input tokens (DashScope/Anthropic
|
|
11
|
+
* threshold semantics — the whole request bills at the tier it lands in).
|
|
12
|
+
*/
|
|
13
|
+
import type { ModelRate } from './pricing.js';
|
|
14
|
+
export declare const RATE_CARD_GENERATED_AT = "2026-08-15";
|
|
15
|
+
export declare const RATE_CARD: readonly ModelRate[];
|
|
16
|
+
//# sourceMappingURL=rate-card.generated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-card.generated.d.ts","sourceRoot":"","sources":["../src/rate-card.generated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD,eAAO,MAAM,SAAS,EAAE,SAAS,SAAS,EA6EzC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const RATE_CARD_GENERATED_AT = '2026-08-15';
|
|
2
|
+
export const RATE_CARD = [
|
|
3
|
+
{
|
|
4
|
+
"provider": "qwen",
|
|
5
|
+
"idPrefix": "qwen3.6-flash",
|
|
6
|
+
"label": "Qwen 3.6 Flash",
|
|
7
|
+
"contextTokens": 1000000,
|
|
8
|
+
"tiers": [
|
|
9
|
+
{
|
|
10
|
+
"upToInputTokens": null,
|
|
11
|
+
"inputPer1M": "0.1875",
|
|
12
|
+
"outputPer1M": "1.125",
|
|
13
|
+
"cacheReadPer1M": null,
|
|
14
|
+
"cacheWritePer1M": "0.234375"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"sources": [
|
|
18
|
+
"models.dev:alibaba/qwen3.6-flash"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"provider": "qwen",
|
|
23
|
+
"idPrefix": "qwen3.8-max",
|
|
24
|
+
"label": "Qwen 3.8 Max",
|
|
25
|
+
"contextTokens": 991808,
|
|
26
|
+
"tiers": [
|
|
27
|
+
{
|
|
28
|
+
"upToInputTokens": null,
|
|
29
|
+
"inputPer1M": "2",
|
|
30
|
+
"outputPer1M": "6",
|
|
31
|
+
"cacheReadPer1M": "0.25",
|
|
32
|
+
"cacheWritePer1M": "2.5"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"sources": [
|
|
36
|
+
"models.dev:alibaba/qwen3.8-max",
|
|
37
|
+
"litellm:dashscope/qwen3.8-max",
|
|
38
|
+
"https://www.alibabacloud.com/help/en/model-studio/models"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"provider": "anthropic",
|
|
43
|
+
"idPrefix": "claude-sonnet-5",
|
|
44
|
+
"label": "Claude Sonnet 5",
|
|
45
|
+
"contextTokens": 1000000,
|
|
46
|
+
"tiers": [
|
|
47
|
+
{
|
|
48
|
+
"upToInputTokens": null,
|
|
49
|
+
"inputPer1M": "2",
|
|
50
|
+
"outputPer1M": "10",
|
|
51
|
+
"cacheReadPer1M": "0.2",
|
|
52
|
+
"cacheWritePer1M": "2.5"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"sources": [
|
|
56
|
+
"models.dev:anthropic/claude-sonnet-5",
|
|
57
|
+
"litellm:claude-sonnet-5"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"provider": "anthropic",
|
|
62
|
+
"idPrefix": "claude-opus-5",
|
|
63
|
+
"label": "Claude Opus 5",
|
|
64
|
+
"contextTokens": 1000000,
|
|
65
|
+
"tiers": [
|
|
66
|
+
{
|
|
67
|
+
"upToInputTokens": null,
|
|
68
|
+
"inputPer1M": "5",
|
|
69
|
+
"outputPer1M": "25",
|
|
70
|
+
"cacheReadPer1M": "0.5",
|
|
71
|
+
"cacheWritePer1M": "6.25"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"sources": [
|
|
75
|
+
"models.dev:anthropic/claude-opus-5",
|
|
76
|
+
"litellm:claude-opus-5"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
//# sourceMappingURL=rate-card.generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-card.generated.js","sourceRoot":"","sources":["../src/rate-card.generated.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD,MAAM,CAAC,MAAM,SAAS,GAAyB;IAC9C;QACC,UAAU,EAAE,MAAM;QAClB,UAAU,EAAE,eAAe;QAC3B,OAAO,EAAE,gBAAgB;QACzB,eAAe,EAAE,OAAO;QACxB,OAAO,EAAE;YACR;gBACC,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,QAAQ;gBACtB,aAAa,EAAE,OAAO;gBACtB,gBAAgB,EAAE,IAAI;gBACtB,iBAAiB,EAAE,UAAU;aAC7B;SACD;QACD,SAAS,EAAE;YACV,kCAAkC;SAClC;KACD;IACD;QACC,UAAU,EAAE,MAAM;QAClB,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,cAAc;QACvB,eAAe,EAAE,MAAM;QACvB,OAAO,EAAE;YACR;gBACC,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,GAAG;gBACjB,aAAa,EAAE,GAAG;gBAClB,gBAAgB,EAAE,MAAM;gBACxB,iBAAiB,EAAE,KAAK;aACxB;SACD;QACD,SAAS,EAAE;YACV,gCAAgC;YAChC,+BAA+B;YAC/B,0DAA0D;SAC1D;KACD;IACD;QACC,UAAU,EAAE,WAAW;QACvB,UAAU,EAAE,iBAAiB;QAC7B,OAAO,EAAE,iBAAiB;QAC1B,eAAe,EAAE,OAAO;QACxB,OAAO,EAAE;YACR;gBACC,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,GAAG;gBACjB,aAAa,EAAE,IAAI;gBACnB,gBAAgB,EAAE,KAAK;gBACvB,iBAAiB,EAAE,KAAK;aACxB;SACD;QACD,SAAS,EAAE;YACV,sCAAsC;YACtC,yBAAyB;SACzB;KACD;IACD;QACC,UAAU,EAAE,WAAW;QACvB,UAAU,EAAE,eAAe;QAC3B,OAAO,EAAE,eAAe;QACxB,eAAe,EAAE,OAAO;QACxB,OAAO,EAAE;YACR;gBACC,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,GAAG;gBACjB,aAAa,EAAE,IAAI;gBACnB,gBAAgB,EAAE,KAAK;gBACvB,iBAAiB,EAAE,MAAM;aACzB;SACD;QACD,SAAS,EAAE;YACV,oCAAoC;YACpC,uBAAuB;SACvB;KACD;CACD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CredentialEnv } from './resolve.js';
|
|
2
|
+
/** Attribution as the wire takes it — flat, five keys, scalar values. */
|
|
3
|
+
export type WireAttribution = Readonly<Record<string, string | number | boolean>>;
|
|
4
|
+
export interface RequestExtrasInput {
|
|
5
|
+
readonly attribution: WireAttribution;
|
|
6
|
+
readonly env: CredentialEnv;
|
|
7
|
+
}
|
|
8
|
+
/** The per-request headers a row wants, or none. Pure. */
|
|
9
|
+
export declare function requestHeadersFor(provider: string, input: RequestExtrasInput): Record<string, string>;
|
|
10
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,yEAAyE;AACzE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;AAElF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;CAC5B;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBrG"}
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-request extras, by row — what a host attaches to one call beyond the prompt.
|
|
3
|
+
*
|
|
4
|
+
* The only consumer today is Cloudflare's AI Gateway, whose extras are all headers:
|
|
5
|
+
*
|
|
6
|
+
* - `cf-aig-metadata` the call's attribution, as JSON. The gateway keeps at
|
|
7
|
+
* most FIVE keys, which is why `ModelAttribution` is
|
|
8
|
+
* fixed at five — a sixth would drop silently here.
|
|
9
|
+
* - `cf-aig-collect-log-payload` `false`: the gateway logs token counts, model, cost
|
|
10
|
+
* and duration but never the prompt or the answer. The
|
|
11
|
+
* D-54 subprocessor promise, made true on the wire.
|
|
12
|
+
* - `cf-aig-gateway-id` which gateway, when the platform has named one
|
|
13
|
+
* (`CLOUDFLARE_AI_GATEWAY_ID`); absent, the account's
|
|
14
|
+
* default gateway serves the request.
|
|
15
|
+
*
|
|
16
|
+
* What is deliberately NOT here: a spend limit. That is gateway configuration keyed on
|
|
17
|
+
* the same metadata (per tenant, per day), set by the operator and enforced by the
|
|
18
|
+
* gateway — a second guard beneath the host's own budget policy, never the only one.
|
|
19
|
+
*
|
|
20
|
+
* A row with no `request` kind sends nothing extra, and the host never learns which
|
|
21
|
+
* case it was in.
|
|
22
|
+
*/
|
|
23
|
+
import { providerRow } from './providers.js';
|
|
24
|
+
/** The per-request headers a row wants, or none. Pure. */
|
|
25
|
+
export function requestHeadersFor(provider, input) {
|
|
26
|
+
const row = providerRow(provider);
|
|
27
|
+
if (!row || row.kind !== 'compatible' || !row.request)
|
|
28
|
+
return {};
|
|
29
|
+
switch (row.request) {
|
|
30
|
+
case 'cloudflare-gateway': {
|
|
31
|
+
const keys = Object.keys(input.attribution);
|
|
32
|
+
if (keys.length > 5) {
|
|
33
|
+
throw new RangeError(`AI Gateway keeps five metadata keys; ${keys.length} were given (${keys.join(', ')})`);
|
|
34
|
+
}
|
|
35
|
+
const gateway = input.env['CLOUDFLARE_AI_GATEWAY_ID'];
|
|
36
|
+
return {
|
|
37
|
+
'cf-aig-metadata': JSON.stringify(input.attribution),
|
|
38
|
+
'cf-aig-collect-log-payload': 'false',
|
|
39
|
+
...(gateway ? { 'cf-aig-gateway-id': gateway } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAW7C,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,KAAyB;IAC5E,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACjE,QAAQ,GAAG,CAAC,OAAO,EAAE,CAAC;QACrB,KAAK,oBAAoB,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,UAAU,CACnB,wCAAwC,IAAI,CAAC,MAAM,gBAAgB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACrF,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YACtD,OAAO;gBACN,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC;gBACpD,4BAA4B,EAAE,OAAO;gBACrC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD,CAAC;QACH,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
|
+
import { type ProviderSpec } from './providers.js';
|
|
3
|
+
export declare class ProviderError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** A credential environment — `process.env`, a Worker's bindings, or a test's literal. */
|
|
7
|
+
export type CredentialEnv = Readonly<Record<string, string | undefined>>;
|
|
8
|
+
/** What a provider row resolves to against one environment. */
|
|
9
|
+
export interface ProviderCredentials {
|
|
10
|
+
readonly apiKey?: string;
|
|
11
|
+
/** Effective endpoint (compatible rows) or override (direct rows); '' when unset and required. */
|
|
12
|
+
readonly baseUrl: string;
|
|
13
|
+
/** Which env var supplied the endpoint, or 'default'. */
|
|
14
|
+
readonly baseUrlSource: 'default' | string;
|
|
15
|
+
/** Env vars the row needs and the environment does not carry. */
|
|
16
|
+
readonly missing: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read a provider's credential and endpoint from an environment — pure, no
|
|
20
|
+
* process access, so a Worker and the CLI resolve identically from their own
|
|
21
|
+
* sources.
|
|
22
|
+
*/
|
|
23
|
+
export declare function credentialsFrom(provider: string, env: CredentialEnv): ProviderCredentials;
|
|
24
|
+
/** The `createX` shape every AI SDK direct provider exports. */
|
|
25
|
+
export type DirectFactory = (config: {
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
baseURL?: string;
|
|
28
|
+
}) => (modelId: string) => LanguageModel;
|
|
29
|
+
/** Direct-provider factories keyed by provider name — `{ anthropic: createAnthropic }`. */
|
|
30
|
+
export type DirectFactories = Readonly<Record<string, DirectFactory | undefined>>;
|
|
31
|
+
export interface ResolvedModel {
|
|
32
|
+
readonly model: LanguageModel;
|
|
33
|
+
/** `provider/modelId` — the label ledgers and banners show. */
|
|
34
|
+
readonly label: string;
|
|
35
|
+
readonly provider: string;
|
|
36
|
+
readonly modelId: string;
|
|
37
|
+
/**
|
|
38
|
+
* The endpoint this model will actually call, and which env var chose it.
|
|
39
|
+
* Surfaced because a silently-ignored base-URL override is otherwise
|
|
40
|
+
* invisible until a request fails — and then it looks like a bad key.
|
|
41
|
+
*/
|
|
42
|
+
readonly endpoint?: string;
|
|
43
|
+
readonly endpointSource?: 'default' | string;
|
|
44
|
+
}
|
|
45
|
+
export interface CreateModelOptions {
|
|
46
|
+
/** Direct-provider factories the host loaded. A direct row with no factory here is refused. */
|
|
47
|
+
readonly factories?: DirectFactories;
|
|
48
|
+
/**
|
|
49
|
+
* Turns a missing env var into the host's own advice ("set it in .env" vs
|
|
50
|
+
* "set it as a worker secret"). Default names the variable and stops.
|
|
51
|
+
*/
|
|
52
|
+
readonly describeMissing?: (envVar: string, provider: string) => string;
|
|
53
|
+
/** Refuse rows marked `local` — a hosted runtime cannot dial localhost. */
|
|
54
|
+
readonly hosted?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build the model. Every refusal is a `ProviderError` with the next step in it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createModel(spec: string, env: CredentialEnv, options?: CreateModelOptions): ResolvedModel;
|
|
60
|
+
/** The row for a provider, or a ProviderError naming the known ones. */
|
|
61
|
+
export declare function providerSpec(provider: string): ProviderSpec;
|
|
62
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxC,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI3E,qBAAa,aAAc,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAED,0FAA0F;AAC1F,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEzE,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3C,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,mBAAmB,CAyBzF;AAED,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC;AAElH,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC;AAElF,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,+FAA+F;IAC/F,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACxE,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO,GAAE,kBAAuB,GAAG,aAAa,CAuD7G;AASD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAI3D"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `provider:model` + credentials → a `LanguageModel`, with the host deciding
|
|
3
|
+
* WHERE credentials come from and HOW provider packages are loaded.
|
|
4
|
+
*
|
|
5
|
+
* Two hosts exist today and they differ on exactly those two axes: the builder
|
|
6
|
+
* CLI reads `process.env` and imports provider packages dynamically; a Worker
|
|
7
|
+
* reads its bindings and can only import statically. So the resolver takes the
|
|
8
|
+
* credential environment as a plain record and the direct-provider factories as
|
|
9
|
+
* a map — the table (providers.ts) says which env var and which factory NAME,
|
|
10
|
+
* the host supplies the values. Nothing here touches `process`.
|
|
11
|
+
*
|
|
12
|
+
* Compatible providers need no injection: `@ai-sdk/openai-compatible` is a
|
|
13
|
+
* dependency of this package, imported statically, and safe everywhere.
|
|
14
|
+
*/
|
|
15
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
16
|
+
import { PROVIDERS, providerRow } from './providers.js';
|
|
17
|
+
import { qwenCacheFetch } from './qwen-cache.js';
|
|
18
|
+
import { parseModelSpec } from './spec.js';
|
|
19
|
+
export class ProviderError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'ProviderError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read a provider's credential and endpoint from an environment — pure, no
|
|
27
|
+
* process access, so a Worker and the CLI resolve identically from their own
|
|
28
|
+
* sources.
|
|
29
|
+
*/
|
|
30
|
+
export function credentialsFrom(provider, env) {
|
|
31
|
+
const spec = providerRow(provider);
|
|
32
|
+
if (!spec)
|
|
33
|
+
throw unknownProvider(provider);
|
|
34
|
+
const missing = [];
|
|
35
|
+
const apiKey = spec.envVar ? env[spec.envVar] : undefined;
|
|
36
|
+
if (spec.envVar && !apiKey)
|
|
37
|
+
missing.push(spec.envVar);
|
|
38
|
+
if (spec.kind === 'direct') {
|
|
39
|
+
const override = spec.baseUrlEnv ? env[spec.baseUrlEnv] : undefined;
|
|
40
|
+
return {
|
|
41
|
+
...(apiKey ? { apiKey } : {}),
|
|
42
|
+
baseUrl: override ?? '',
|
|
43
|
+
baseUrlSource: override && spec.baseUrlEnv ? spec.baseUrlEnv : 'default',
|
|
44
|
+
missing,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const override = env[spec.baseUrlEnv];
|
|
48
|
+
const baseUrl = override ?? spec.baseUrl;
|
|
49
|
+
if (!baseUrl)
|
|
50
|
+
missing.push(spec.baseUrlEnv);
|
|
51
|
+
return {
|
|
52
|
+
...(apiKey ? { apiKey } : {}),
|
|
53
|
+
baseUrl,
|
|
54
|
+
baseUrlSource: override ? spec.baseUrlEnv : 'default',
|
|
55
|
+
missing,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the model. Every refusal is a `ProviderError` with the next step in it.
|
|
60
|
+
*/
|
|
61
|
+
export function createModel(spec, env, options = {}) {
|
|
62
|
+
const { provider, modelId } = parseModelSpec(spec);
|
|
63
|
+
const row = providerRow(provider);
|
|
64
|
+
if (!row)
|
|
65
|
+
throw unknownProvider(provider);
|
|
66
|
+
if (!modelId)
|
|
67
|
+
throw new ProviderError(`no model id in ${JSON.stringify(spec)}`);
|
|
68
|
+
if (options.hosted && row.hosting.local) {
|
|
69
|
+
throw new ProviderError(`${provider} is local-machine inference — it does not exist on a hosted runtime. Use a local host for local models.`);
|
|
70
|
+
}
|
|
71
|
+
const creds = credentialsFrom(provider, env);
|
|
72
|
+
if (creds.missing.length) {
|
|
73
|
+
const describe = options.describeMissing ?? ((v) => `${v} is not set.`);
|
|
74
|
+
throw new ProviderError(`provider ${provider} is not configured.\n` + creds.missing.map((v) => ` ${describe(v, provider)}`).join('\n'));
|
|
75
|
+
}
|
|
76
|
+
const label = `${provider}/${modelId}`;
|
|
77
|
+
if (row.kind === 'direct') {
|
|
78
|
+
const create = options.factories?.[provider];
|
|
79
|
+
if (!create) {
|
|
80
|
+
throw new ProviderError(`provider ${provider} needs ${row.pkg} (${row.createFactory}) loaded by the host — it is not wired here.`);
|
|
81
|
+
}
|
|
82
|
+
const built = create({
|
|
83
|
+
...(creds.apiKey ? { apiKey: creds.apiKey } : {}),
|
|
84
|
+
...(creds.baseUrl ? { baseURL: creds.baseUrl } : {}),
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
model: built(modelId),
|
|
88
|
+
label,
|
|
89
|
+
provider,
|
|
90
|
+
modelId,
|
|
91
|
+
...(creds.baseUrl ? { endpoint: creds.baseUrl, endpointSource: creds.baseUrlSource } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const factory = createOpenAICompatible({
|
|
95
|
+
name: provider,
|
|
96
|
+
baseURL: creds.baseUrl,
|
|
97
|
+
// Local runtimes ignore this but the client still wants a value.
|
|
98
|
+
apiKey: creds.apiKey ?? 'local',
|
|
99
|
+
...(row.wire === 'qwen-cache' ? { fetch: qwenCacheFetch() } : {}),
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
model: factory(modelId),
|
|
103
|
+
label,
|
|
104
|
+
provider,
|
|
105
|
+
modelId,
|
|
106
|
+
endpoint: creds.baseUrl,
|
|
107
|
+
endpointSource: creds.baseUrlSource,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function unknownProvider(provider) {
|
|
111
|
+
return new ProviderError(`unknown provider ${JSON.stringify(provider)}. Known: ${Object.keys(PROVIDERS).join(', ')}\n` +
|
|
112
|
+
` Use provider:model, e.g. anthropic:claude-opus-5 — or provider:auto for a declared pair`);
|
|
113
|
+
}
|
|
114
|
+
/** The row for a provider, or a ProviderError naming the known ones. */
|
|
115
|
+
export function providerSpec(provider) {
|
|
116
|
+
const row = providerRow(provider);
|
|
117
|
+
if (!row)
|
|
118
|
+
throw unknownProvider(provider);
|
|
119
|
+
return row;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/spec.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `provider:model` grammar — the one string every host, picker, ledger and
|
|
3
|
+
* rate-card lookup agrees on.
|
|
4
|
+
*
|
|
5
|
+
* `anthropic:claude-opus-5`, `cloudflare:@cf/zai-org/glm-5.2`, `scaleway:…`. A
|
|
6
|
+
* bare id with no colon is an Anthropic model, because that is the default
|
|
7
|
+
* provider — and the default lives HERE, once, so `claude-opus-5` and
|
|
8
|
+
* `anthropic:claude-opus-5` land on one meter rather than two.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_PROVIDER = "anthropic";
|
|
11
|
+
export interface ModelSpec {
|
|
12
|
+
readonly provider: string;
|
|
13
|
+
readonly modelId: string;
|
|
14
|
+
}
|
|
15
|
+
/** Split a spec; the provider defaults, the model id may be empty (callers refuse that). */
|
|
16
|
+
export declare function parseModelSpec(spec: string): ModelSpec;
|
|
17
|
+
/** `provider:modelId` with the default provider made explicit. */
|
|
18
|
+
export declare function normalizeModelSpec(spec: string): string;
|
|
19
|
+
/** The provider half alone — for error messages and per-provider lookups. */
|
|
20
|
+
export declare function providerOf(spec: string): string;
|
|
21
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAItD;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
|
package/dist/spec.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `provider:model` grammar — the one string every host, picker, ledger and
|
|
3
|
+
* rate-card lookup agrees on.
|
|
4
|
+
*
|
|
5
|
+
* `anthropic:claude-opus-5`, `cloudflare:@cf/zai-org/glm-5.2`, `scaleway:…`. A
|
|
6
|
+
* bare id with no colon is an Anthropic model, because that is the default
|
|
7
|
+
* provider — and the default lives HERE, once, so `claude-opus-5` and
|
|
8
|
+
* `anthropic:claude-opus-5` land on one meter rather than two.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_PROVIDER = 'anthropic';
|
|
11
|
+
/** Split a spec; the provider defaults, the model id may be empty (callers refuse that). */
|
|
12
|
+
export function parseModelSpec(spec) {
|
|
13
|
+
const idx = spec.indexOf(':');
|
|
14
|
+
if (idx === -1)
|
|
15
|
+
return { provider: DEFAULT_PROVIDER, modelId: spec };
|
|
16
|
+
return { provider: spec.slice(0, idx), modelId: spec.slice(idx + 1) };
|
|
17
|
+
}
|
|
18
|
+
/** `provider:modelId` with the default provider made explicit. */
|
|
19
|
+
export function normalizeModelSpec(spec) {
|
|
20
|
+
const { provider, modelId } = parseModelSpec(spec);
|
|
21
|
+
return `${provider}:${modelId}`;
|
|
22
|
+
}
|
|
23
|
+
/** The provider half alone — for error messages and per-provider lookups. */
|
|
24
|
+
export function providerOf(spec) {
|
|
25
|
+
return parseModelSpec(spec).provider;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=spec.js.map
|
package/dist/spec.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAO5C,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACrE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,GAAG,QAAQ,IAAI,OAAO,EAAE,CAAC;AACjC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;AACtC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@substrat-run/model-providers",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The model-provider seam: one `provider:model` grammar, a table of providers (Anthropic, OpenAI, Google, Mistral, Qwen, Cloudflare, Scaleway, Ollama, any OpenAI-compatible endpoint), the hosting disclosure per provider, the generated rate card and the list-price math over it. Cloudflare is one row, not the design (#1054).",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/substrat-run/substrat.git",
|
|
9
|
+
"directory": "packages/model-providers"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://substrat.net/reference/model-providers",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"update-rate-card": "node scripts/update-rate-card.mjs"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@ai-sdk/openai-compatible": "3.0.30",
|
|
32
|
+
"@substrat-run/contracts": "workspace:^",
|
|
33
|
+
"ai": "7.0.62"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^7.0.0",
|
|
37
|
+
"vitest": "catalog:"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|