@jambonz/llm 0.6.5 → 0.6.7

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.
@@ -174,12 +174,20 @@ async function* streamFromOpenAI(client, req, options) {
174
174
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
175
175
  }
176
176
  if (chunk.usage) {
177
- const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
177
+ const details = chunk.usage.prompt_tokens_details;
178
+ const cachedTokens = details?.cached_tokens;
179
+ const cacheWriteTokens = details?.cache_write_tokens;
178
180
  usage = {
179
- ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens - (cachedTokens ?? 0) } : {},
181
+ ...chunk.usage.prompt_tokens !== void 0 ? {
182
+ inputTokens: Math.max(
183
+ 0,
184
+ chunk.usage.prompt_tokens - (cachedTokens ?? 0) - (cacheWriteTokens ?? 0)
185
+ )
186
+ } : {},
180
187
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
181
188
  ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {},
182
- ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {}
189
+ ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {},
190
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
183
191
  };
184
192
  }
185
193
  const choice = chunk.choices?.[0];
@@ -3570,6 +3578,179 @@ var huggingfaceFactory = {
3570
3578
  create: () => new HuggingfaceAdapter()
3571
3579
  };
3572
3580
 
3581
+ // src/adapters/openrouter/manifest.ts
3582
+ var OPENROUTER_KNOWN_MODELS = [
3583
+ {
3584
+ id: "anthropic/claude-haiku-5.5",
3585
+ displayName: "Claude Haiku 5.5 (OpenRouter)",
3586
+ capabilities: {
3587
+ streaming: true,
3588
+ tools: true,
3589
+ vision: true,
3590
+ systemPrompt: true,
3591
+ maxContextTokens: 1e6
3592
+ }
3593
+ },
3594
+ {
3595
+ id: "anthropic/claude-sonnet-5.5",
3596
+ displayName: "Claude Sonnet 5.5 (OpenRouter)",
3597
+ capabilities: {
3598
+ streaming: true,
3599
+ tools: true,
3600
+ vision: true,
3601
+ systemPrompt: true,
3602
+ maxContextTokens: 1e6
3603
+ }
3604
+ },
3605
+ {
3606
+ id: "openai/gpt-5.4-mini",
3607
+ displayName: "GPT-5.4 Mini (OpenRouter)",
3608
+ capabilities: {
3609
+ streaming: true,
3610
+ tools: true,
3611
+ vision: true,
3612
+ systemPrompt: true,
3613
+ maxContextTokens: 4e5
3614
+ }
3615
+ },
3616
+ {
3617
+ id: "openai/gpt-4.1-mini",
3618
+ displayName: "GPT-4.1 Mini (OpenRouter)",
3619
+ capabilities: {
3620
+ streaming: true,
3621
+ tools: true,
3622
+ vision: true,
3623
+ systemPrompt: true,
3624
+ maxContextTokens: 1047576
3625
+ }
3626
+ },
3627
+ {
3628
+ id: "google/gemini-3.5-flash",
3629
+ displayName: "Gemini 3.5 Flash (OpenRouter)",
3630
+ capabilities: {
3631
+ streaming: true,
3632
+ tools: true,
3633
+ vision: true,
3634
+ systemPrompt: true,
3635
+ maxContextTokens: 1048576
3636
+ }
3637
+ },
3638
+ {
3639
+ id: "meta-llama/llama-3.3-70b-instruct",
3640
+ displayName: "Llama 3.3 70B Instruct (OpenRouter)",
3641
+ capabilities: {
3642
+ streaming: true,
3643
+ tools: true,
3644
+ vision: false,
3645
+ systemPrompt: true,
3646
+ maxContextTokens: 131072
3647
+ }
3648
+ }
3649
+ ];
3650
+ var openRouterManifest = {
3651
+ vendor: "openrouter",
3652
+ displayName: "OpenRouter",
3653
+ authKinds: [
3654
+ {
3655
+ kind: "apiKey",
3656
+ displayName: "API Key",
3657
+ fields: [
3658
+ {
3659
+ name: "apiKey",
3660
+ label: "API Key",
3661
+ type: "password",
3662
+ required: true,
3663
+ help: "Get an API key from https://openrouter.ai/settings/keys"
3664
+ },
3665
+ {
3666
+ name: "baseURL",
3667
+ label: "Base URL",
3668
+ type: "url",
3669
+ required: false,
3670
+ default: "https://openrouter.ai/api/v1",
3671
+ help: "Override only for proxies. Default points at OpenRouter's production endpoint."
3672
+ }
3673
+ ]
3674
+ }
3675
+ ],
3676
+ knownModels: OPENROUTER_KNOWN_MODELS,
3677
+ // /models is public; the adapter enriches it with OpenRouter's own capability metadata.
3678
+ supportsModelListing: true,
3679
+ docsUrl: "https://openrouter.ai/docs/api_reference/overview"
3680
+ };
3681
+
3682
+ // src/adapters/openrouter/adapter.ts
3683
+ var OPENROUTER_METADATA_EXTRACTOR = makeMetadataExtractor([
3684
+ { header: "x-generation-id", key: "request_id" }
3685
+ ]);
3686
+ var OPENROUTER_ATTRIBUTION_HEADERS = {
3687
+ "HTTP-Referer": "https://jambonz.org",
3688
+ "X-OpenRouter-Title": "jambonz"
3689
+ };
3690
+ var OpenRouterAdapter = class extends OpenAIAdapter {
3691
+ vendor = openRouterManifest.vendor;
3692
+ init(auth, client) {
3693
+ super.init(
3694
+ {
3695
+ ...auth,
3696
+ baseURL: auth.baseURL ?? "https://openrouter.ai/api/v1"
3697
+ },
3698
+ client
3699
+ );
3700
+ this.client = this.ensureClient().withOptions({
3701
+ defaultHeaders: OPENROUTER_ATTRIBUTION_HEADERS
3702
+ });
3703
+ }
3704
+ stream(req) {
3705
+ return streamFromOpenAI(this.ensureClient(), req, {
3706
+ knownModels: this.knownModels(),
3707
+ vendorMetadataExtractor: OPENROUTER_METADATA_EXTRACTOR,
3708
+ // Verified accepted by OpenRouter (forwarded to OpenAI-family upstreams).
3709
+ includeCacheKey: true,
3710
+ openRouterAutoCache: this.isOpenRouter
3711
+ });
3712
+ }
3713
+ async listAvailableModels() {
3714
+ const result = await this.ensureClient().models.list();
3715
+ const out = [];
3716
+ for (const m of result.data) {
3717
+ if (m.id.endsWith(":batch")) continue;
3718
+ const outputs = m.architecture?.output_modalities;
3719
+ if (outputs && !outputs.includes("text")) continue;
3720
+ const known = this.knownModels().find((k) => k.id === m.id);
3721
+ if (known) {
3722
+ out.push(known);
3723
+ continue;
3724
+ }
3725
+ out.push({
3726
+ id: m.id,
3727
+ ...m.name ? { displayName: m.name } : {},
3728
+ capabilities: {
3729
+ streaming: true,
3730
+ tools: m.supported_parameters?.includes("tools") ?? true,
3731
+ vision: m.architecture?.input_modalities?.includes("image") ?? false,
3732
+ systemPrompt: true,
3733
+ ...m.context_length ? { maxContextTokens: m.context_length } : {}
3734
+ }
3735
+ });
3736
+ }
3737
+ return out;
3738
+ }
3739
+ async testCredential() {
3740
+ await this.ensureClient().get("/key");
3741
+ }
3742
+ knownModels() {
3743
+ return openRouterManifest.knownModels;
3744
+ }
3745
+ };
3746
+
3747
+ // src/adapters/openrouter/factory.ts
3748
+ var openRouterFactory = {
3749
+ vendor: openRouterManifest.vendor,
3750
+ manifest: openRouterManifest,
3751
+ create: () => new OpenRouterAdapter()
3752
+ };
3753
+
3573
3754
  // src/adapters/index.ts
3574
3755
  registerAdapter(openAIFactory);
3575
3756
  registerAdapter(deepseekFactory);
@@ -3586,6 +3767,7 @@ registerAdapter(bedrockFactory);
3586
3767
  registerAdapter(azureOpenAIFactory);
3587
3768
  registerAdapter(groqFactory);
3588
3769
  registerAdapter(huggingfaceFactory);
3770
+ registerAdapter(openRouterFactory);
3589
3771
 
3590
3772
  // src/index.ts
3591
3773
  async function createLlm(args) {