@plurnk/plurnk-providers 0.1.0 → 0.1.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 +7 -2
- package/SPEC.md +39 -7
- package/dist/Mock.d.ts +3 -3
- package/dist/Mock.d.ts.map +1 -1
- package/dist/Mock.js +4 -1
- package/dist/Mock.js.map +1 -1
- package/dist/OpenAICompat.d.ts +27 -0
- package/dist/OpenAICompat.d.ts.map +1 -0
- package/dist/OpenAICompat.js +84 -0
- package/dist/OpenAICompat.js.map +1 -0
- package/dist/ProviderRegistry.d.ts.map +1 -1
- package/dist/ProviderRegistry.js +8 -5
- package/dist/ProviderRegistry.js.map +1 -1
- package/dist/env.d.ts +4 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +25 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/openaiStream.d.ts +28 -0
- package/dist/openaiStream.d.ts.map +1 -0
- package/dist/openaiStream.js +103 -0
- package/dist/openaiStream.js.map +1 -0
- package/dist/standardProviders.d.ts +19 -0
- package/dist/standardProviders.d.ts.map +1 -0
- package/dist/standardProviders.js +88 -0
- package/dist/standardProviders.js.map +1 -0
- package/dist/tokenizers.d.ts +6 -0
- package/dist/tokenizers.d.ts.map +1 -0
- package/dist/tokenizers.js +36 -0
- package/dist/tokenizers.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -9,8 +9,13 @@ Framework + contract for `@plurnk/plurnk-providers-*` sibling packages (LLM tran
|
|
|
9
9
|
|
|
10
10
|
## Exports
|
|
11
11
|
|
|
12
|
-
- `Provider`, `ChatMessage`, `ProviderResponse`, `ProviderAssistant`, `ProviderUsage`, `ProviderFactory` — types.
|
|
13
|
-
- `parseAliasesFromEnv`, `resolveActiveAlias
|
|
12
|
+
- `Provider`, `ChatMessage`, `ProviderResponse`, `ProviderAssistant`, `ProviderUsage`, `FinishReason`, `ProviderFactory` — types.
|
|
13
|
+
- `parseAliasesFromEnv`, `resolveActiveAlias` — alias-cascade resolution (pure env-parsing). Provider instantiation (`instantiateProvider`, `loadActiveProvider`) is consumer-side; see SPEC §5.
|
|
14
|
+
- `OpenAICompatProvider` (+ `OpenAICompatConfig`, `ReasoningStyle`, `effortFromBudget`) — shared OpenAI-compatible transport spine; siblings extend it (SPEC §11).
|
|
15
|
+
- `chatCompletionStream`, `OpenAiHttpError`, `StreamResponse` — the shared SSE client.
|
|
16
|
+
- `parseRequiredInt`, `parseOptionalInt`, `requireEnv` — env helpers.
|
|
17
|
+
- `tokenizerFor`, `tokenizerByPublisher`, `parseTokenizerFamily` (+ `TokenizerFamily`, `CountTokens`) — synchronous tokenizer strategies.
|
|
18
|
+
- `STANDARD_PROVIDERS`, `isStandardProvider`, `standardProviderFromEnv` — pure-config OpenAI-compatible providers (no sibling package needed).
|
|
14
19
|
- `Mock` — reference implementation + test fixture (dual-purpose).
|
|
15
20
|
|
|
16
21
|
## Tests
|
package/SPEC.md
CHANGED
|
@@ -55,7 +55,9 @@ interface ProviderUsage {
|
|
|
55
55
|
|
|
56
56
|
### Promises
|
|
57
57
|
|
|
58
|
-
- `assistant.content` is the **verbatim** model emission. Consumer parses via `@plurnk/plurnk-grammar` — providers MUST NOT parse.
|
|
58
|
+
- `assistant.content` is the **verbatim** model emission. Consumer parses via `@plurnk/plurnk-grammar` — providers MUST NOT parse. plurnk uses a **tools-in-body** design: tool invocations are expressed as plurnk DSL *inside the message content*, never via a provider's native tool-calling API, so `content` is always raw text and providers never request, parse, or translate native tool calls.[^tools]
|
|
59
|
+
|
|
60
|
+
[^tools]: A provider that wants to drive native tool-calling (OpenAI `tool_calls`, Anthropic `tool_use`) would have to normalize those emissions back into a plurnk DSL string at the boundary. No provider does this and it is out of scope for v0 — tools-in-body sidesteps the whole problem. The clause is recorded only so a future native-tools mode has a defined contract.
|
|
59
61
|
- `assistant.usage` is authoritative. Fill `0`s when the wire response omits a breakdown.
|
|
60
62
|
- `countTokens` is **synchronous**, returns a non-negative integer, deterministic for the same input.
|
|
61
63
|
- `costFor` is **pure**, returns pico-USD non-negative integer. Returns `0` for siblings with no known rates (local Ollama, generic OpenAI-compat shims).
|
|
@@ -78,7 +80,7 @@ class OpenAI {
|
|
|
78
80
|
}
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
The
|
|
83
|
+
The consumer's instantiation path calls `mod.default.fromEnv(env, alias.model)` generically (§5).
|
|
82
84
|
|
|
83
85
|
`fromEnv` MAY be sync or async; return type `Provider | Promise<Provider>`.
|
|
84
86
|
|
|
@@ -102,14 +104,20 @@ PLURNK_MODEL_opus=openrouter/anthropic/claude-opus-latest
|
|
|
102
104
|
PLURNK_MODEL=gemma
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
First path segment names the provider
|
|
107
|
+
First path segment names the provider; rest is the model identifier (may contain `/` for tri-level providers like openrouter's `publisher/model`).
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
This package's exported resolution surface:
|
|
108
110
|
|
|
109
111
|
- `parseAliasesFromEnv(env)` — extracts alias entries.
|
|
110
112
|
- `resolveActiveAlias(env)` — `{ alias, provider, model } | null`.
|
|
111
|
-
- `
|
|
112
|
-
|
|
113
|
+
- `standardProviderFromEnv(name, env, model)` / `isStandardProvider(name)` — tier-1 instantiation (below).
|
|
114
|
+
|
|
115
|
+
**Two-tier provider resolution.** A provider name resolves in this order:
|
|
116
|
+
|
|
117
|
+
1. **Standard provider** (`§11`) — if `isStandardProvider(name)`, the framework instantiates it directly via `standardProviderFromEnv(name, env, model)`. No sibling package exists or is imported. Covers every plain OpenAI-compatible endpoint (`openai`, `groq`, `deepseek`, `mistral`, `together`, `fireworks`, `deepinfra`, …).
|
|
118
|
+
2. **Bespoke sibling** — otherwise, dynamic-import `@plurnk/plurnk-providers-<provider>` and call `fromEnv(env, model)`. Reserved for providers with real runtime surface: a catalog/pricing probe or a non-OpenAI wire shape (`openrouter`, `ollama`, `google`, `xai`, `cloudflare`, the planned `anthropic`/`bedrock`/`vertex`/`cohere`).
|
|
119
|
+
|
|
120
|
+
Tier 1 lives here because it needs no package resolution. **Tier 2 is not shipped by this package.** Node's `import()` resolves specifiers relative to the calling module, so the dynamic-import step has to run where the sibling packages are actually installed — the consumer's `node_modules`, not this package's. The consumer owns it: in plurnk-service it is `src/core/ProviderInstantiate.ts` (`instantiateProvider` / `loadActiveProvider`). That code should try `standardProviderFromEnv` first and dynamic-import `@plurnk/plurnk-providers-<provider>` only when it returns `null`.
|
|
113
121
|
|
|
114
122
|
## §6 Engine → provider guarantees (consumer side)
|
|
115
123
|
|
|
@@ -124,7 +132,7 @@ Framework helpers (`./ProviderRegistry.ts`):
|
|
|
124
132
|
- **No DB access.** Provider never touches `node:sqlite` or storage layers.
|
|
125
133
|
- **No service access.** No imports from `@plurnk/plurnk-service`.
|
|
126
134
|
- **No grammar runtime dep.** Type-imports from `@plurnk/plurnk-grammar` are fine; invoking `PlurnkParser.parse` is consumer-side.
|
|
127
|
-
- **Raw `content`.**
|
|
135
|
+
- **Raw `content`.** Returned verbatim. Tools are expressed in-body as plurnk DSL (see §2); providers do not use native tool-calling.
|
|
128
136
|
- **Atomic.** One `generate` call resolves with one complete `ProviderResponse`. No streaming partial resolves (v0).
|
|
129
137
|
- **Honors `signal`.** Aborted calls reject; resources free; no orphaned connections.
|
|
130
138
|
- **Single model.** One provider instance speaks to one model.
|
|
@@ -180,3 +188,27 @@ A sibling package satisfies the contract when:
|
|
|
180
188
|
12. No runtime import of `@plurnk/plurnk-grammar` parser entry points.
|
|
181
189
|
|
|
182
190
|
Sibling-specific behavioral tests (wire-format compliance, model-family quirks, retry logic) live in each package's own test surface.
|
|
191
|
+
|
|
192
|
+
## §11 Shared OpenAI-compatible machinery
|
|
193
|
+
|
|
194
|
+
The framework ships the transport spine every OpenAI-compatible provider had been duplicating. Build a sibling *on top of these* — don't re-implement them.
|
|
195
|
+
|
|
196
|
+
- **`OpenAICompatProvider`** — a `Provider` implementation built by composition. Its `generate` does the universal work (merge `signal` with a `PLURNK_FETCH_TIMEOUT` deadline, stream the completion, map `usage`, normalize `finishReason` to the §2 set, assemble the response). Per-provider deltas arrive as config:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
new OpenAICompatProvider({
|
|
200
|
+
model, url, // fully-resolved chat-completions URL
|
|
201
|
+
fetchTimeoutMs,
|
|
202
|
+
headers, // fully-resolved request headers (incl. auth)
|
|
203
|
+
contextSize, // number | null
|
|
204
|
+
reasonBudget, reasoningStyle, // "none" | "think" | "include_reasoning" | "effort"
|
|
205
|
+
countTokens, costFor, // strategies; default heuristic / free
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
- **`chatCompletionStream` / `OpenAiHttpError` / `StreamResponse`** — the SSE client. One shared copy.
|
|
210
|
+
- **`parseRequiredInt` / `parseOptionalInt` / `requireEnv`** — env helpers; each takes a provider `label` for error prefixing.
|
|
211
|
+
- **`tokenizerFor(family)` / `tokenizerByPublisher(model, table, index)` / `parseTokenizerFamily(...)`** — synchronous tokenizer strategies (`heuristic` | `cl100k` | `llama`) and per-publisher dispatch for relay providers.
|
|
212
|
+
- **`effortFromBudget(budget)`** — the shared `PLURNK_REASON` → `low|medium|high` breakpoints.
|
|
213
|
+
|
|
214
|
+
A **bespoke sibling** therefore reduces to a thin class whose `fromEnv` probes whatever it needs (model catalog, pricing, context window), builds the config, and returns `new OpenAICompatProvider(config)`. A **standard provider** (§5 tier 1) needs no sibling at all — it's a frozen entry in `STANDARD_PROVIDERS` describing its key var, base URL, reasoning style, and tokenizer; `standardProviderFromEnv` does the rest.
|
package/dist/Mock.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { PlurnkStatement } from "@plurnk/plurnk-grammar";
|
|
2
|
-
import type { ChatMessage, Provider, ProviderAssistant, ProviderUsage } from "./types.ts";
|
|
2
|
+
import type { ChatMessage, FinishReason, Provider, ProviderAssistant, ProviderUsage } from "./types.ts";
|
|
3
3
|
export type MockAssistant = {
|
|
4
4
|
content: string;
|
|
5
5
|
reasoning: string | null;
|
|
6
6
|
usage?: ProviderUsage;
|
|
7
|
-
finishReason?:
|
|
7
|
+
finishReason?: FinishReason;
|
|
8
8
|
model?: string;
|
|
9
9
|
ops?: PlurnkStatement[];
|
|
10
10
|
};
|
|
@@ -26,7 +26,7 @@ export default class Mock implements Provider {
|
|
|
26
26
|
get model(): string;
|
|
27
27
|
countTokens(text: string): number;
|
|
28
28
|
costFor(_usage: ProviderUsage): number;
|
|
29
|
-
generate(
|
|
29
|
+
generate({ signal }: {
|
|
30
30
|
messages: ChatMessage[];
|
|
31
31
|
signal?: AbortSignal;
|
|
32
32
|
}): Promise<{
|
package/dist/Mock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mock.d.ts","sourceRoot":"","sources":["../src/Mock.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Mock.d.ts","sourceRoot":"","sources":["../src/Mock.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExG,MAAM,MAAM,aAAa,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,GAAG,CAAC,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,SAAS,EAAE,aAAa,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAGF,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IAAE,GAAG,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC;AAEpF,QAAA,MAAM,aAAa,EAAE,aAAiE,CAAC;AAEvF,MAAM,CAAC,OAAO,OAAO,IAAK,YAAW,QAAQ;;gBAI7B,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,YAAY,EAAE,CAAA;KAAE;IAKjG,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAA8B;IAC9D,IAAI,KAAK,IAAI,MAAM,CAAmB;IAItC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAKjC,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IAEhC,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,qBAAqB,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAkBnJ,IAAI,SAAS,IAAI,MAAM,CAA+B;CACzD;AAED,OAAO,EAAE,aAAa,IAAI,gBAAgB,EAAE,CAAC"}
|
package/dist/Mock.js
CHANGED
|
@@ -21,7 +21,10 @@ export default class Mock {
|
|
|
21
21
|
}
|
|
22
22
|
// Mock is free.
|
|
23
23
|
costFor(_usage) { return 0; }
|
|
24
|
-
async generate(
|
|
24
|
+
async generate({ signal }) {
|
|
25
|
+
// Honor abort before consuming the queue — an aborted call makes no
|
|
26
|
+
// "wire call" and must not exhaust a queued response (SPEC §10.8).
|
|
27
|
+
signal?.throwIfAborted();
|
|
25
28
|
const next = this.#queue.shift();
|
|
26
29
|
if (next === undefined)
|
|
27
30
|
throw new Error("Mock provider exhausted: no more queued responses");
|
package/dist/Mock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mock.js","sourceRoot":"","sources":["../src/Mock.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,2CAA2C;AAwB3C,MAAM,aAAa,GAAkB,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAEvF,MAAM,CAAC,OAAO,OAAO,IAAI;IACrB,YAAY,CAAgB;IAC5B,MAAM,CAAiB;IAEvB,YAAY,EAAE,WAAW,EAAE,SAAS,EAA6D;QAC7F,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,WAAW,KAAoB,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9D,IAAI,KAAK,KAAa,OAAO,MAAM,CAAC,CAAC,CAAC;IAEtC,sEAAsE;IACtE,8BAA8B;IAC9B,WAAW,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,gBAAgB;IAChB,OAAO,CAAC,MAAqB,IAAY,OAAO,CAAC,CAAC,CAAC,CAAC;IAEpD,KAAK,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"Mock.js","sourceRoot":"","sources":["../src/Mock.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,2CAA2C;AAwB3C,MAAM,aAAa,GAAkB,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAEvF,MAAM,CAAC,OAAO,OAAO,IAAI;IACrB,YAAY,CAAgB;IAC5B,MAAM,CAAiB;IAEvB,YAAY,EAAE,WAAW,EAAE,SAAS,EAA6D;QAC7F,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,WAAW,KAAoB,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9D,IAAI,KAAK,KAAa,OAAO,MAAM,CAAC,CAAC,CAAC;IAEtC,sEAAsE;IACtE,8BAA8B;IAC9B,WAAW,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,gBAAgB;IAChB,OAAO,CAAC,MAAqB,IAAY,OAAO,CAAC,CAAC,CAAC,CAAC;IAEpD,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAqD;QACxE,oEAAoE;QACpE,mEAAmE;QACnE,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,IAAI,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QAC7F,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QACzB,MAAM,SAAS,GAA0B;YACrC,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,aAAa;YAC/B,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,MAAM;YACtC,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM;YACxB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjD,CAAC;QACF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;IAClE,CAAC;IAED,IAAI,SAAS,KAAa,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;CACzD;AAED,OAAO,EAAE,aAAa,IAAI,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChatMessage, Provider, ProviderResponse, ProviderUsage } from "./types.ts";
|
|
2
|
+
export type ReasoningStyle = "none" | "think" | "include_reasoning" | "effort";
|
|
3
|
+
export type OpenAICompatConfig = {
|
|
4
|
+
model: string;
|
|
5
|
+
url: string;
|
|
6
|
+
fetchTimeoutMs: number;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
contextSize?: number | null;
|
|
9
|
+
reasonBudget?: number;
|
|
10
|
+
reasoningStyle?: ReasoningStyle;
|
|
11
|
+
countTokens?: (text: string) => number;
|
|
12
|
+
costFor?: (usage: ProviderUsage) => number;
|
|
13
|
+
};
|
|
14
|
+
export declare const effortFromBudget: (budget: number) => "low" | "medium" | "high";
|
|
15
|
+
export default class OpenAICompatProvider implements Provider {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(config: OpenAICompatConfig);
|
|
18
|
+
get contextSize(): number | null;
|
|
19
|
+
get model(): string;
|
|
20
|
+
countTokens(text: string): number;
|
|
21
|
+
costFor(usage: ProviderUsage): number;
|
|
22
|
+
generate({ messages, signal }: {
|
|
23
|
+
messages: ChatMessage[];
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}): Promise<ProviderResponse>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=OpenAICompat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAICompat.d.ts","sourceRoot":"","sources":["../src/OpenAICompat.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAgB,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQvG,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,mBAAmB,GAAG,QAAQ,CAAC;AAE/E,MAAM,MAAM,kBAAkB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAC;CAC9C,CAAC;AASF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,KAAK,GAAG,QAAQ,GAAG,MAIpE,CAAC;AAIF,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,QAAQ;;gBAW7C,MAAM,EAAE,kBAAkB;IAYtC,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAA8B;IAC9D,IAAI,KAAK,IAAI,MAAM,CAAwB;IAE3C,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IACjC,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM;IAY/B,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;QAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA4BrH"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Shared OpenAI-compatible provider. Implements the universal generate()
|
|
2
|
+
// spine — signal merging, the SSE call, usage mapping, finishReason
|
|
3
|
+
// normalization, response assembly — that every sibling had duplicated.
|
|
4
|
+
//
|
|
5
|
+
// Composition, not inheritance: the per-provider deltas (resolved URL, auth
|
|
6
|
+
// headers, reasoning translation style, tokenizer, cost) arrive as config.
|
|
7
|
+
// A sibling's fromEnv probes whatever it needs (catalog, pricing, context
|
|
8
|
+
// window), builds the config, and returns `new OpenAICompatProvider(config)`.
|
|
9
|
+
// Pure-config providers come from ./standardProviders.ts with no sibling at all.
|
|
10
|
+
import { chatCompletionStream } from "./openaiStream.js";
|
|
11
|
+
// SPEC §2 closed set. Wire values outside it (provider-specific or absent)
|
|
12
|
+
// collapse to null — the consumer treats null as "no signal".
|
|
13
|
+
const FINISH_REASONS = new Set(["stop", "length", "tool_calls", "content_filter"]);
|
|
14
|
+
const normalizeFinishReason = (raw) => raw !== null && FINISH_REASONS.has(raw) ? raw : null;
|
|
15
|
+
// Shared budget→effort breakpoints (xai and google had identical copies).
|
|
16
|
+
export const effortFromBudget = (budget) => {
|
|
17
|
+
if (budget <= 1000)
|
|
18
|
+
return "low";
|
|
19
|
+
if (budget <= 4000)
|
|
20
|
+
return "medium";
|
|
21
|
+
return "high";
|
|
22
|
+
};
|
|
23
|
+
const heuristicTokens = (text) => (text.length === 0 ? 0 : Math.ceil(text.length / 4));
|
|
24
|
+
export default class OpenAICompatProvider {
|
|
25
|
+
#model;
|
|
26
|
+
#url;
|
|
27
|
+
#fetchTimeoutMs;
|
|
28
|
+
#headers;
|
|
29
|
+
#contextSize;
|
|
30
|
+
#reasonBudget;
|
|
31
|
+
#reasoningStyle;
|
|
32
|
+
#countTokens;
|
|
33
|
+
#costFor;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.#model = config.model;
|
|
36
|
+
this.#url = config.url;
|
|
37
|
+
this.#fetchTimeoutMs = config.fetchTimeoutMs;
|
|
38
|
+
this.#headers = config.headers ?? {};
|
|
39
|
+
this.#contextSize = config.contextSize ?? null;
|
|
40
|
+
this.#reasonBudget = config.reasonBudget ?? 0;
|
|
41
|
+
this.#reasoningStyle = config.reasoningStyle ?? "none";
|
|
42
|
+
this.#countTokens = config.countTokens ?? heuristicTokens;
|
|
43
|
+
this.#costFor = config.costFor ?? (() => 0);
|
|
44
|
+
}
|
|
45
|
+
get contextSize() { return this.#contextSize; }
|
|
46
|
+
get model() { return this.#model; }
|
|
47
|
+
countTokens(text) { return this.#countTokens(text); }
|
|
48
|
+
costFor(usage) { return this.#costFor(usage); }
|
|
49
|
+
#reasoningBody() {
|
|
50
|
+
if (this.#reasonBudget <= 0)
|
|
51
|
+
return {};
|
|
52
|
+
switch (this.#reasoningStyle) {
|
|
53
|
+
case "think": return { think: true };
|
|
54
|
+
case "include_reasoning": return { include_reasoning: true };
|
|
55
|
+
case "effort": return { reasoning_effort: effortFromBudget(this.#reasonBudget) };
|
|
56
|
+
case "none": return {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async generate({ messages, signal }) {
|
|
60
|
+
// Reject before any wire call when already aborted (SPEC §10.8).
|
|
61
|
+
signal?.throwIfAborted();
|
|
62
|
+
const timeoutSignal = AbortSignal.timeout(this.#fetchTimeoutMs);
|
|
63
|
+
const effectiveSignal = signal !== undefined ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
64
|
+
const body = { model: this.#model, messages, ...this.#reasoningBody() };
|
|
65
|
+
const raw = await chatCompletionStream({ url: this.#url, headers: this.#headers, body, signal: effectiveSignal });
|
|
66
|
+
const usage = {
|
|
67
|
+
prompt: raw.usage?.prompt_tokens ?? 0,
|
|
68
|
+
completion: raw.usage?.completion_tokens ?? 0,
|
|
69
|
+
cached: raw.usage?.cached_tokens ?? 0,
|
|
70
|
+
total: raw.usage?.total_tokens ?? 0,
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
assistant: {
|
|
74
|
+
content: raw.content,
|
|
75
|
+
reasoning: raw.reasoning_content.length > 0 ? raw.reasoning_content : null,
|
|
76
|
+
usage,
|
|
77
|
+
finishReason: normalizeFinishReason(raw.finish_reason),
|
|
78
|
+
model: raw.model ?? this.#model,
|
|
79
|
+
},
|
|
80
|
+
assistantRaw: raw,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=OpenAICompat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAICompat.js","sourceRoot":"","sources":["../src/OpenAICompat.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderRegistry.d.ts","sourceRoot":"","sources":["../src/ProviderRegistry.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,mBAAmB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"ProviderRegistry.d.ts","sourceRoot":"","sources":["../src/ProviderRegistry.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,mBAAmB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,aAAa,EAkBvF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,aAAa,GAAG,IAKzF,CAAC"}
|
package/dist/ProviderRegistry.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// env-parsing helpers.
|
|
10
10
|
export const parseAliasesFromEnv = (env = process.env) => {
|
|
11
11
|
const out = [];
|
|
12
|
+
const seen = new Set();
|
|
12
13
|
for (const [key, value] of Object.entries(env)) {
|
|
13
14
|
if (value === undefined || value.length === 0)
|
|
14
15
|
continue;
|
|
@@ -20,11 +21,13 @@ export const parseAliasesFromEnv = (env = process.env) => {
|
|
|
20
21
|
const slash = value.indexOf("/");
|
|
21
22
|
if (slash <= 0)
|
|
22
23
|
continue;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const alias = aliasRaw.toLowerCase();
|
|
25
|
+
// Aliases are case-folded, so PLURNK_MODEL_opus and PLURNK_MODEL_OPUS
|
|
26
|
+
// collide. Surface the ambiguity rather than silently picking one.
|
|
27
|
+
if (seen.has(alias))
|
|
28
|
+
throw new Error(`Duplicate provider alias "${alias}": multiple PLURNK_MODEL_* keys case-fold to the same alias. Rename one.`);
|
|
29
|
+
seen.add(alias);
|
|
30
|
+
out.push({ alias, provider: value.slice(0, slash), model: value.slice(slash + 1) });
|
|
28
31
|
}
|
|
29
32
|
return out;
|
|
30
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderRegistry.js","sourceRoot":"","sources":["../src/ProviderRegistry.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,oEAAoE;AACpE,6EAA6E;AAC7E,yEAAyE;AACzE,uBAAuB;AAIvB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAmB,EAAE;IACzF,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACxD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QAC/C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,IAAI,CAAC;YAAE,SAAS;QACzB,GAAG,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ProviderRegistry.js","sourceRoot":"","sources":["../src/ProviderRegistry.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,oEAAoE;AACpE,6EAA6E;AAC7E,yEAAyE;AACzE,uBAAuB;AAIvB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAmB,EAAE;IACzF,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACxD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QAC/C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,IAAI,CAAC;YAAE,SAAS;QACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACrC,sEAAsE;QACtE,mEAAmE;QACnE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,0EAA0E,CAAC,CAAC;QACnJ,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAwB,EAAE;IAC7F,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC;IAClC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAC3E,CAAC,CAAC"}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const parseRequiredInt: (raw: string | undefined, name: string, label: string) => number;
|
|
2
|
+
export declare const parseOptionalInt: (raw: string | undefined, name: string, label: string) => number | null;
|
|
3
|
+
export declare const requireEnv: (raw: string | undefined, name: string, label: string) => string;
|
|
4
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAKvF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAAM,GAAG,IAKhG,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAGjF,CAAC"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Env-parsing helpers shared by every provider's fromEnv factory. Each was
|
|
2
|
+
// copy-pasted per sibling with only the error-message prefix differing; the
|
|
3
|
+
// `label` parameter (the provider name) restores that prefix from one source.
|
|
4
|
+
export const parseRequiredInt = (raw, name, label) => {
|
|
5
|
+
if (raw === undefined || raw.length === 0)
|
|
6
|
+
throw new Error(`${label} provider: ${name} must be set`);
|
|
7
|
+
const n = Number(raw);
|
|
8
|
+
if (!Number.isFinite(n))
|
|
9
|
+
throw new Error(`${label} provider: ${name} must be a number (got "${raw}")`);
|
|
10
|
+
return n;
|
|
11
|
+
};
|
|
12
|
+
export const parseOptionalInt = (raw, name, label) => {
|
|
13
|
+
if (raw === undefined || raw.length === 0)
|
|
14
|
+
return null;
|
|
15
|
+
const n = Number(raw);
|
|
16
|
+
if (!Number.isFinite(n))
|
|
17
|
+
throw new Error(`${label} provider: ${name} must be a number (got "${raw}")`);
|
|
18
|
+
return n;
|
|
19
|
+
};
|
|
20
|
+
export const requireEnv = (raw, name, label) => {
|
|
21
|
+
if (raw === undefined || raw.length === 0)
|
|
22
|
+
throw new Error(`${label} provider: ${name} must be set`);
|
|
23
|
+
return raw;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AAE9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAuB,EAAE,IAAY,EAAE,KAAa,EAAU,EAAE;IAC7F,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,IAAI,cAAc,CAAC,CAAC;IACrG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,IAAI,2BAA2B,GAAG,IAAI,CAAC,CAAC;IACvG,OAAO,CAAC,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAuB,EAAE,IAAY,EAAE,KAAa,EAAiB,EAAE;IACpG,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,IAAI,2BAA2B,GAAG,IAAI,CAAC,CAAC;IACvG,OAAO,CAAC,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAuB,EAAE,IAAY,EAAE,KAAa,EAAU,EAAE;IACvF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,IAAI,cAAc,CAAC,CAAC;IACrG,OAAO,GAAG,CAAC;AACf,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
export type { ChatMessage, Provider, ProviderAlias, ProviderAssistant, ProviderFactory, ProviderResponse, ProviderUsage, } from "./types.ts";
|
|
1
|
+
export type { ChatMessage, FinishReason, Provider, ProviderAlias, ProviderAssistant, ProviderFactory, ProviderResponse, ProviderUsage, } from "./types.ts";
|
|
2
2
|
export { parseAliasesFromEnv, resolveActiveAlias, } from "./ProviderRegistry.ts";
|
|
3
|
+
export { default as OpenAICompatProvider, effortFromBudget } from "./OpenAICompat.ts";
|
|
4
|
+
export type { OpenAICompatConfig, ReasoningStyle } from "./OpenAICompat.ts";
|
|
5
|
+
export { chatCompletionStream, OpenAiHttpError } from "./openaiStream.ts";
|
|
6
|
+
export type { StreamResponse } from "./openaiStream.ts";
|
|
7
|
+
export { parseRequiredInt, parseOptionalInt, requireEnv } from "./env.ts";
|
|
8
|
+
export { tokenizerFor, tokenizerByPublisher, parseTokenizerFamily } from "./tokenizers.ts";
|
|
9
|
+
export type { TokenizerFamily, CountTokens } from "./tokenizers.ts";
|
|
10
|
+
export { STANDARD_PROVIDERS, isStandardProvider, standardProviderFromEnv } from "./standardProviders.ts";
|
|
3
11
|
export { default as Mock } from "./Mock.ts";
|
|
4
12
|
export type { MockAssistant, MockResponse, MockReturnedAssistant } from "./Mock.ts";
|
|
5
13
|
export { mockDefaultUsage } from "./Mock.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,WAAW,EACX,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,aAAa,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,mBAAmB,EACnB,kBAAkB,GACrB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,aAAa,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,mBAAmB,EACnB,kBAAkB,GACrB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACtF,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEzG,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,WAAW,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export { parseAliasesFromEnv, resolveActiveAlias, } from "./ProviderRegistry.js";
|
|
2
|
+
// Shared OpenAI-compatible transport machinery — the spine every sibling
|
|
3
|
+
// extends and the basis for ./standardProviders.ts.
|
|
4
|
+
export { default as OpenAICompatProvider, effortFromBudget } from "./OpenAICompat.js";
|
|
5
|
+
export { chatCompletionStream, OpenAiHttpError } from "./openaiStream.js";
|
|
6
|
+
export { parseRequiredInt, parseOptionalInt, requireEnv } from "./env.js";
|
|
7
|
+
export { tokenizerFor, tokenizerByPublisher, parseTokenizerFamily } from "./tokenizers.js";
|
|
8
|
+
export { STANDARD_PROVIDERS, isStandardProvider, standardProviderFromEnv } from "./standardProviders.js";
|
|
2
9
|
export { default as Mock } from "./Mock.js";
|
|
3
10
|
export { mockDefaultUsage } from "./Mock.js";
|
|
4
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACH,mBAAmB,EACnB,kBAAkB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,yEAAyE;AACzE,oDAAoD;AACpD,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEtF,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE1E,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE3F,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEzG,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type StreamRequest = {
|
|
2
|
+
url: string;
|
|
3
|
+
headers: Record<string, string>;
|
|
4
|
+
body: Record<string, unknown>;
|
|
5
|
+
signal: AbortSignal;
|
|
6
|
+
};
|
|
7
|
+
export type StreamResponse = {
|
|
8
|
+
model: string | null;
|
|
9
|
+
content: string;
|
|
10
|
+
reasoning_content: string;
|
|
11
|
+
finish_reason: string | null;
|
|
12
|
+
usage: {
|
|
13
|
+
prompt_tokens?: number;
|
|
14
|
+
completion_tokens?: number;
|
|
15
|
+
total_tokens?: number;
|
|
16
|
+
cached_tokens?: number;
|
|
17
|
+
} | null;
|
|
18
|
+
chunkMetadata: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
export declare class OpenAiHttpError extends Error {
|
|
21
|
+
readonly status: number;
|
|
22
|
+
readonly body: string;
|
|
23
|
+
readonly retryAfter: number | null;
|
|
24
|
+
constructor(status: number, body: string, retryAfter: number | null);
|
|
25
|
+
}
|
|
26
|
+
export declare const chatCompletionStream: ({ url, headers, body, signal }: StreamRequest) => Promise<StreamResponse>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=openaiStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiStream.d.ts","sourceRoot":"","sources":["../src/openaiStream.ts"],"names":[],"mappings":"AAMA,KAAK,aAAa,GAAG;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C,CAAC;AAEF,qBAAa,eAAgB,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;gBACvB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;CAMtE;AAWD,eAAO,MAAM,oBAAoB,GAAU,gCAAgC,aAAa,KAAG,OAAO,CAAC,cAAc,CAmEhH,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// SSE client for OpenAI-compatible /chat/completions. Streaming keeps long
|
|
2
|
+
// completions alive through CDN proxies; the aggregated result is returned as
|
|
3
|
+
// one StreamResponse (the Provider contract is atomic — no partial resolves).
|
|
4
|
+
// Adapted from rummy's proven implementation; previously copy-pasted byte-for-
|
|
5
|
+
// byte into every @plurnk/plurnk-providers-* sibling, now shared from here.
|
|
6
|
+
export class OpenAiHttpError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
body;
|
|
9
|
+
retryAfter;
|
|
10
|
+
constructor(status, body, retryAfter) {
|
|
11
|
+
super(`OpenAI ${status} - ${body}`);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.body = body;
|
|
14
|
+
this.retryAfter = retryAfter;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const parseRetryAfter = (header) => {
|
|
18
|
+
if (header === null)
|
|
19
|
+
return null;
|
|
20
|
+
const asInt = Number.parseInt(header, 10);
|
|
21
|
+
if (Number.isFinite(asInt))
|
|
22
|
+
return asInt * 1000;
|
|
23
|
+
const asDate = Date.parse(header);
|
|
24
|
+
if (Number.isFinite(asDate))
|
|
25
|
+
return Math.max(0, asDate - Date.now());
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
export const chatCompletionStream = async ({ url, headers, body, signal }) => {
|
|
29
|
+
const requestBody = { ...body, stream: true, stream_options: { include_usage: true } };
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
33
|
+
body: JSON.stringify(requestBody),
|
|
34
|
+
signal,
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errorBody = await response.text();
|
|
38
|
+
throw new OpenAiHttpError(response.status, errorBody, parseRetryAfter(response.headers.get("retry-after")));
|
|
39
|
+
}
|
|
40
|
+
if (response.body === null)
|
|
41
|
+
throw new Error("OpenAI response body is null");
|
|
42
|
+
const reader = response.body.getReader();
|
|
43
|
+
const decoder = new TextDecoder();
|
|
44
|
+
let buffer = "";
|
|
45
|
+
let content = "";
|
|
46
|
+
let reasoning_content = "";
|
|
47
|
+
let usage = null;
|
|
48
|
+
let model = null;
|
|
49
|
+
let finish_reason = null;
|
|
50
|
+
const chunkMetadata = {};
|
|
51
|
+
while (true) {
|
|
52
|
+
const { done, value } = await reader.read();
|
|
53
|
+
if (done)
|
|
54
|
+
break;
|
|
55
|
+
buffer += decoder.decode(value, { stream: true });
|
|
56
|
+
const lines = buffer.split("\n");
|
|
57
|
+
buffer = lines.pop() ?? "";
|
|
58
|
+
for (const rawLine of lines) {
|
|
59
|
+
const line = rawLine.trim();
|
|
60
|
+
if (!line.startsWith("data:"))
|
|
61
|
+
continue;
|
|
62
|
+
const payload = line.slice(5).trimStart();
|
|
63
|
+
if (payload === "[DONE]" || payload === "")
|
|
64
|
+
continue;
|
|
65
|
+
let chunk;
|
|
66
|
+
try {
|
|
67
|
+
chunk = JSON.parse(payload);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (typeof chunk.model === "string")
|
|
73
|
+
model = chunk.model;
|
|
74
|
+
if (chunk.usage !== undefined && chunk.usage !== null)
|
|
75
|
+
usage = chunk.usage;
|
|
76
|
+
for (const [k, v] of Object.entries(chunk)) {
|
|
77
|
+
if (k === "choices" || k === "usage")
|
|
78
|
+
continue;
|
|
79
|
+
chunkMetadata[k] = v;
|
|
80
|
+
}
|
|
81
|
+
const choices = chunk.choices;
|
|
82
|
+
const choice = choices?.[0];
|
|
83
|
+
if (choice === undefined)
|
|
84
|
+
continue;
|
|
85
|
+
if (typeof choice.finish_reason === "string")
|
|
86
|
+
finish_reason = choice.finish_reason;
|
|
87
|
+
const delta = choice.delta;
|
|
88
|
+
if (delta === undefined)
|
|
89
|
+
continue;
|
|
90
|
+
if (typeof delta.content === "string")
|
|
91
|
+
content += delta.content;
|
|
92
|
+
// Reasoning surfaces under different field names per provider.
|
|
93
|
+
if (typeof delta.reasoning_content === "string")
|
|
94
|
+
reasoning_content += delta.reasoning_content;
|
|
95
|
+
if (typeof delta.reasoning === "string")
|
|
96
|
+
reasoning_content += delta.reasoning;
|
|
97
|
+
if (typeof delta.thinking === "string")
|
|
98
|
+
reasoning_content += delta.thinking;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { model, content, reasoning_content, finish_reason, usage, chunkMetadata };
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=openaiStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiStream.js","sourceRoot":"","sources":["../src/openaiStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Provider } from "./types.ts";
|
|
2
|
+
import { type ReasoningStyle } from "./OpenAICompat.ts";
|
|
3
|
+
import { type TokenizerFamily } from "./tokenizers.ts";
|
|
4
|
+
type StandardProviderSpec = {
|
|
5
|
+
apiKeyVar: string;
|
|
6
|
+
apiKeyRequired: boolean;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
baseUrlVar?: string;
|
|
9
|
+
chatPath: string;
|
|
10
|
+
flexBaseStrip?: boolean;
|
|
11
|
+
reasoningStyle: ReasoningStyle;
|
|
12
|
+
tokenizerDefault: TokenizerFamily;
|
|
13
|
+
tokenizerEnvVar: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const STANDARD_PROVIDERS: Readonly<Record<string, StandardProviderSpec>>;
|
|
16
|
+
export declare const isStandardProvider: (name: string) => boolean;
|
|
17
|
+
export declare const standardProviderFromEnv: (name: string, env: NodeJS.ProcessEnv, model: string) => Provider | null;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=standardProviders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardProviders.d.ts","sourceRoot":"","sources":["../src/standardProviders.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAA6B,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9E,OAAO,EAAsC,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE3F,KAAK,oBAAoB,GAAG;IAGxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IAGxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,QAAQ,EAAE,MAAM,CAAC;IAIjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,eAAe,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;CAC3B,CAAC;AAGF,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAuC5E,CAAC;AAEH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAAqC,CAAC;AAcxF,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,CAAC,UAAU,EAAE,OAAO,MAAM,KAAG,QAAQ,GAAG,IAuBxG,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Pure-config OpenAI-compatible providers. A provider qualifies as "standard"
|
|
2
|
+
// when it has no unique runtime surface — no catalog probe, no pricing fetch,
|
|
3
|
+
// no bespoke wire shape — so it reduces to: an env var for the key, a base
|
|
4
|
+
// URL, a reasoning-translation style, and a tokenizer. Such providers need NO
|
|
5
|
+
// sibling package; the framework instantiates them directly.
|
|
6
|
+
//
|
|
7
|
+
// Two-tier resolution (SPEC §5): the consumer tries standardProviderFromEnv
|
|
8
|
+
// first, then falls back to dynamic-importing @plurnk/plurnk-providers-<name>
|
|
9
|
+
// for the bespoke ones (openrouter, ollama, google, xai, cloudflare, ...).
|
|
10
|
+
import OpenAICompatProvider from "./OpenAICompat.js";
|
|
11
|
+
import { parseRequiredInt, parseOptionalInt, requireEnv } from "./env.js";
|
|
12
|
+
import { parseTokenizerFamily, tokenizerFor } from "./tokenizers.js";
|
|
13
|
+
// Frozen so a downstream can't mutate the shared table.
|
|
14
|
+
export const STANDARD_PROVIDERS = Object.freeze({
|
|
15
|
+
// Generic OpenAI-compatible endpoint (OpenAI proper, llama-server, vLLM,
|
|
16
|
+
// LM Studio, or any chat-completions shim). Operator supplies the base.
|
|
17
|
+
// Replaces the former @plurnk/plurnk-providers-openai sibling verbatim.
|
|
18
|
+
openai: {
|
|
19
|
+
apiKeyVar: "OPENAI_API_KEY", apiKeyRequired: false,
|
|
20
|
+
baseUrlVar: "OPENAI_BASE_URL", chatPath: "/v1/chat/completions", flexBaseStrip: true,
|
|
21
|
+
reasoningStyle: "think", tokenizerDefault: "heuristic", tokenizerEnvVar: "OPENAI_TOKENIZER",
|
|
22
|
+
},
|
|
23
|
+
groq: {
|
|
24
|
+
apiKeyVar: "GROQ_API_KEY", apiKeyRequired: true,
|
|
25
|
+
baseUrl: "https://api.groq.com/openai/v1", baseUrlVar: "GROQ_BASE_URL", chatPath: "/chat/completions",
|
|
26
|
+
reasoningStyle: "effort", tokenizerDefault: "heuristic", tokenizerEnvVar: "GROQ_TOKENIZER",
|
|
27
|
+
},
|
|
28
|
+
deepseek: {
|
|
29
|
+
apiKeyVar: "DEEPSEEK_API_KEY", apiKeyRequired: true,
|
|
30
|
+
baseUrl: "https://api.deepseek.com/v1", baseUrlVar: "DEEPSEEK_BASE_URL", chatPath: "/chat/completions",
|
|
31
|
+
reasoningStyle: "none", tokenizerDefault: "heuristic", tokenizerEnvVar: "DEEPSEEK_TOKENIZER",
|
|
32
|
+
},
|
|
33
|
+
mistral: {
|
|
34
|
+
apiKeyVar: "MISTRAL_API_KEY", apiKeyRequired: true,
|
|
35
|
+
baseUrl: "https://api.mistral.ai/v1", baseUrlVar: "MISTRAL_BASE_URL", chatPath: "/chat/completions",
|
|
36
|
+
reasoningStyle: "none", tokenizerDefault: "heuristic", tokenizerEnvVar: "MISTRAL_TOKENIZER",
|
|
37
|
+
},
|
|
38
|
+
together: {
|
|
39
|
+
apiKeyVar: "TOGETHER_API_KEY", apiKeyRequired: true,
|
|
40
|
+
baseUrl: "https://api.together.xyz/v1", baseUrlVar: "TOGETHER_BASE_URL", chatPath: "/chat/completions",
|
|
41
|
+
reasoningStyle: "none", tokenizerDefault: "heuristic", tokenizerEnvVar: "TOGETHER_TOKENIZER",
|
|
42
|
+
},
|
|
43
|
+
fireworks: {
|
|
44
|
+
apiKeyVar: "FIREWORKS_API_KEY", apiKeyRequired: true,
|
|
45
|
+
baseUrl: "https://api.fireworks.ai/inference/v1", baseUrlVar: "FIREWORKS_BASE_URL", chatPath: "/chat/completions",
|
|
46
|
+
reasoningStyle: "none", tokenizerDefault: "heuristic", tokenizerEnvVar: "FIREWORKS_TOKENIZER",
|
|
47
|
+
},
|
|
48
|
+
deepinfra: {
|
|
49
|
+
apiKeyVar: "DEEPINFRA_API_KEY", apiKeyRequired: true,
|
|
50
|
+
baseUrl: "https://api.deepinfra.com/v1/openai", baseUrlVar: "DEEPINFRA_BASE_URL", chatPath: "/chat/completions",
|
|
51
|
+
reasoningStyle: "none", tokenizerDefault: "heuristic", tokenizerEnvVar: "DEEPINFRA_TOKENIZER",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
export const isStandardProvider = (name) => name in STANDARD_PROVIDERS;
|
|
55
|
+
const resolveUrl = (spec, env, label) => {
|
|
56
|
+
const override = spec.baseUrlVar !== undefined ? env[spec.baseUrlVar] : undefined;
|
|
57
|
+
const base = override !== undefined && override.length > 0 ? override : spec.baseUrl;
|
|
58
|
+
if (base === undefined || base.length === 0) {
|
|
59
|
+
throw new Error(`${label} provider: ${spec.baseUrlVar ?? "base URL"} must be set`);
|
|
60
|
+
}
|
|
61
|
+
const trimmed = spec.flexBaseStrip === true ? base.replace(/\/v1\/?$/, "") : base.replace(/\/$/, "");
|
|
62
|
+
return `${trimmed}${spec.chatPath}`;
|
|
63
|
+
};
|
|
64
|
+
// Returns a configured Provider, or null when `name` is not a standard
|
|
65
|
+
// provider (so the consumer falls through to dynamic import).
|
|
66
|
+
export const standardProviderFromEnv = (name, env, model) => {
|
|
67
|
+
const spec = STANDARD_PROVIDERS[name];
|
|
68
|
+
if (spec === undefined)
|
|
69
|
+
return null;
|
|
70
|
+
const apiKey = spec.apiKeyRequired
|
|
71
|
+
? requireEnv(env[spec.apiKeyVar], spec.apiKeyVar, name)
|
|
72
|
+
: env[spec.apiKeyVar] ?? "";
|
|
73
|
+
const headers = {};
|
|
74
|
+
if (apiKey.length > 0)
|
|
75
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
76
|
+
const family = parseTokenizerFamily(env[spec.tokenizerEnvVar], spec.tokenizerDefault, spec.tokenizerEnvVar, name);
|
|
77
|
+
return new OpenAICompatProvider({
|
|
78
|
+
model,
|
|
79
|
+
url: resolveUrl(spec, env, name),
|
|
80
|
+
headers,
|
|
81
|
+
contextSize: parseOptionalInt(env.PLURNK_PROVIDER_CONTEXT_SIZE, "PLURNK_PROVIDER_CONTEXT_SIZE", name),
|
|
82
|
+
fetchTimeoutMs: parseRequiredInt(env.PLURNK_FETCH_TIMEOUT, "PLURNK_FETCH_TIMEOUT", name),
|
|
83
|
+
reasonBudget: parseRequiredInt(env.PLURNK_REASON, "PLURNK_REASON", name),
|
|
84
|
+
reasoningStyle: spec.reasoningStyle,
|
|
85
|
+
countTokens: tokenizerFor(family),
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=standardProviders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardProviders.js","sourceRoot":"","sources":["../src/standardProviders.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type TokenizerFamily = "heuristic" | "cl100k" | "llama";
|
|
2
|
+
export type CountTokens = (text: string) => number;
|
|
3
|
+
export declare const tokenizerFor: (family: TokenizerFamily) => CountTokens;
|
|
4
|
+
export declare const parseTokenizerFamily: (raw: string | undefined, fallback: TokenizerFamily, envName: string, label: string) => TokenizerFamily;
|
|
5
|
+
export declare const tokenizerByPublisher: (model: string, table: ReadonlyMap<string, TokenizerFamily>, index?: number) => TokenizerFamily;
|
|
6
|
+
//# sourceMappingURL=tokenizers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizers.d.ts","sourceRoot":"","sources":["../src/tokenizers.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAQnD,eAAO,MAAM,YAAY,GAAI,QAAQ,eAAe,KAAG,WAAiC,CAAC;AAKzF,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,GAAG,SAAS,EAAE,UAAU,eAAe,EAAE,SAAS,MAAM,EAAE,OAAO,MAAM,KAAG,eAKzH,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC7B,OAAO,MAAM,EACb,OAAO,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAC3C,cAAS,KACV,eAGF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Tokenizer strategies shared by every provider's countTokens(). Centralizes
|
|
2
|
+
// the gpt-tokenizer / llama-tokenizer-js deps (each sibling pulled them in
|
|
3
|
+
// separately) and the per-publisher dispatch maps that openrouter and
|
|
4
|
+
// cloudflare had independently re-invented.
|
|
5
|
+
//
|
|
6
|
+
// countTokens is synchronous by contract (SPEC §2), so only client-side
|
|
7
|
+
// tokenizers qualify. "heuristic" (chars/4) is the safe default for any
|
|
8
|
+
// upstream whose tokenizer we can't run locally.
|
|
9
|
+
import { encode as encodeCl100k } from "gpt-tokenizer/encoding/cl100k_base";
|
|
10
|
+
import llamaTokenizer from "llama-tokenizer-js";
|
|
11
|
+
const heuristic = (text) => (text.length === 0 ? 0 : Math.ceil(text.length / 4));
|
|
12
|
+
const cl100k = (text) => (text.length === 0 ? 0 : encodeCl100k(text).length);
|
|
13
|
+
const llama = (text) => (text.length === 0 ? 0 : llamaTokenizer.encode(text).length);
|
|
14
|
+
const STRATEGIES = Object.freeze({ heuristic, cl100k, llama });
|
|
15
|
+
export const tokenizerFor = (family) => STRATEGIES[family];
|
|
16
|
+
// Parse an operator-declared tokenizer override (e.g. OPENAI_TOKENIZER).
|
|
17
|
+
// Accepts a legacy "cl100k_base" spelling for back-compat with the openai
|
|
18
|
+
// sibling's existing env contract.
|
|
19
|
+
export const parseTokenizerFamily = (raw, fallback, envName, label) => {
|
|
20
|
+
if (raw === undefined || raw.length === 0)
|
|
21
|
+
return fallback;
|
|
22
|
+
if (raw === "cl100k_base")
|
|
23
|
+
return "cl100k";
|
|
24
|
+
if (raw === "heuristic" || raw === "cl100k" || raw === "llama")
|
|
25
|
+
return raw;
|
|
26
|
+
throw new Error(`${label} provider: ${envName} must be one of "heuristic", "cl100k", "llama" (got "${raw}")`);
|
|
27
|
+
};
|
|
28
|
+
// Dispatch a tokenizer family from a model id's publisher segment. Relay
|
|
29
|
+
// providers (openrouter, cloudflare) route across many upstream families, so
|
|
30
|
+
// the family is read from the id prefix. `index` selects which "/"-segment
|
|
31
|
+
// holds the publisher (0 for "anthropic/claude…", 1 for "@cf/meta/llama…").
|
|
32
|
+
export const tokenizerByPublisher = (model, table, index = 0) => {
|
|
33
|
+
const publisher = model.split("/")[index];
|
|
34
|
+
return publisher !== undefined ? table.get(publisher) ?? "heuristic" : "heuristic";
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=tokenizers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizers.js","sourceRoot":"","sources":["../src/tokenizers.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,2EAA2E;AAC3E,sEAAsE;AACtE,4CAA4C;AAC5C,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,iDAAiD;AAEjD,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAMhD,MAAM,SAAS,GAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9F,MAAM,MAAM,GAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AAC1F,MAAM,KAAK,GAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AAElG,MAAM,UAAU,GAAmD,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAE/G,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAuB,EAAe,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAEzF,yEAAyE;AACzE,0EAA0E;AAC1E,mCAAmC;AACnC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAuB,EAAE,QAAyB,EAAE,OAAe,EAAE,KAAa,EAAmB,EAAE;IACxI,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3D,IAAI,GAAG,KAAK,aAAa;QAAE,OAAO,QAAQ,CAAC;IAC3C,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC;IAC3E,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,OAAO,wDAAwD,GAAG,IAAI,CAAC,CAAC;AAClH,CAAC,CAAC;AAEF,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAChC,KAAa,EACb,KAA2C,EAC3C,KAAK,GAAG,CAAC,EACM,EAAE;IACjB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;AACvF,CAAC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,11 +8,12 @@ export interface ProviderUsage {
|
|
|
8
8
|
readonly cached: number;
|
|
9
9
|
readonly total: number;
|
|
10
10
|
}
|
|
11
|
+
export type FinishReason = "stop" | "length" | "tool_calls" | "content_filter" | null;
|
|
11
12
|
export interface ProviderAssistant {
|
|
12
13
|
readonly content: string;
|
|
13
14
|
readonly reasoning: string | null;
|
|
14
15
|
readonly usage: ProviderUsage;
|
|
15
|
-
readonly finishReason:
|
|
16
|
+
readonly finishReason: FinishReason;
|
|
16
17
|
readonly model: string;
|
|
17
18
|
}
|
|
18
19
|
export interface ProviderResponse {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAID,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,GAAG,gBAAgB,GAAG,IAAI,CAAC;AAEtF,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAI7F,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAGlC,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAMD,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChF"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plurnk/plurnk-providers",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Framework + contract for the @plurnk/plurnk-providers-* LLM transport family.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"plurnk",
|
|
7
|
+
"llm",
|
|
8
|
+
"provider",
|
|
9
|
+
"tokenizer"
|
|
10
|
+
],
|
|
6
11
|
"homepage": "https://github.com/plurnk/plurnk-providers#readme",
|
|
7
12
|
"bugs": {
|
|
8
13
|
"url": "https://github.com/plurnk/plurnk-providers/issues"
|
|
@@ -41,11 +46,15 @@
|
|
|
41
46
|
"prepare": "npm run build"
|
|
42
47
|
},
|
|
43
48
|
"peerDependencies": {
|
|
44
|
-
"@plurnk/plurnk-grammar": "
|
|
49
|
+
"@plurnk/plurnk-grammar": "0.17.0"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
|
-
"@plurnk/plurnk-grammar": "
|
|
52
|
+
"@plurnk/plurnk-grammar": "0.17.0",
|
|
48
53
|
"@types/node": "^25.8.0",
|
|
49
54
|
"typescript": "^6.0.3"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"gpt-tokenizer": "^3.4.0",
|
|
58
|
+
"llama-tokenizer-js": "^1.2.2"
|
|
50
59
|
}
|
|
51
60
|
}
|