@jambonz/llm 0.4.5 → 0.5.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/dist/index.cjs CHANGED
@@ -131,8 +131,17 @@ async function* streamFromOpenAI(client, req, options) {
131
131
  body[param] = effectiveMaxTokens;
132
132
  }
133
133
  let stream;
134
+ let responseHeaders;
134
135
  try {
135
- stream = await client.chat.completions.create(body, { signal: req.signal });
136
+ const apiPromise = client.chat.completions.create(body, { signal: req.signal });
137
+ const maybeWithResponse = apiPromise.withResponse;
138
+ if (typeof maybeWithResponse === "function") {
139
+ const result = await maybeWithResponse.call(apiPromise);
140
+ stream = result.data;
141
+ responseHeaders = result.response?.headers;
142
+ } else {
143
+ stream = await apiPromise;
144
+ }
136
145
  } catch (err) {
137
146
  if (isAbortError(err)) {
138
147
  yield { type: "end", finishReason: "aborted" };
@@ -227,6 +236,12 @@ async function* streamFromOpenAI(client, req, options) {
227
236
  ...usage ? { usage } : {},
228
237
  ...finalRawReason ? { rawReason: finalRawReason } : {}
229
238
  };
239
+ if (options.vendorMetadataExtractor && responseHeaders) {
240
+ const metadata = options.vendorMetadataExtractor(responseHeaders);
241
+ if (metadata && Object.keys(metadata).length > 0) {
242
+ endEvent.vendorMetadata = metadata;
243
+ }
244
+ }
230
245
  yield endEvent;
231
246
  }
232
247
  function appendOpenAIAssistantToolCall(history, toolCalls) {
@@ -354,6 +369,40 @@ function isAbortError(err) {
354
369
  return e.name === "APIUserAbortError" || e.name === "AbortError" || e.code === "ABORT_ERR" || typeof e.message === "string" && /aborted/i.test(e.message);
355
370
  }
356
371
 
372
+ // src/adapters/_metadata.ts
373
+ function makeMetadataExtractor(fields) {
374
+ return (headers) => {
375
+ const out = {};
376
+ for (const f of fields) {
377
+ const raw = headers.get(f.header);
378
+ if (raw === null || raw === void 0 || raw === "") continue;
379
+ if (f.numeric) {
380
+ const n = Number(raw);
381
+ if (Number.isFinite(n)) out[f.key] = n;
382
+ } else {
383
+ out[f.key] = raw;
384
+ }
385
+ }
386
+ return out;
387
+ };
388
+ }
389
+ function extractMetadata(fields, headers) {
390
+ const headersInstance = headers instanceof Headers ? headers : recordToHeaders(headers);
391
+ return makeMetadataExtractor(fields)(headersInstance);
392
+ }
393
+ function recordToHeaders(record) {
394
+ const h = new Headers();
395
+ for (const [name, value] of Object.entries(record)) {
396
+ if (value === void 0) continue;
397
+ if (Array.isArray(value)) {
398
+ for (const v of value) h.append(name, v);
399
+ } else {
400
+ h.set(name, value);
401
+ }
402
+ }
403
+ return h;
404
+ }
405
+
357
406
  // src/adapters/openai/manifest.ts
358
407
  var OPENAI_KNOWN_MODELS = [
359
408
  {
@@ -455,6 +504,12 @@ var openAIManifest = {
455
504
  };
456
505
 
457
506
  // src/adapters/openai/adapter.ts
507
+ var OPENAI_METADATA_EXTRACTOR = makeMetadataExtractor([
508
+ { header: "x-request-id", key: "request_id" },
509
+ { header: "openai-processing-ms", key: "processing_ms", numeric: true },
510
+ { header: "x-ratelimit-remaining-requests", key: "requests_remaining", numeric: true },
511
+ { header: "x-ratelimit-remaining-tokens", key: "tokens_remaining", numeric: true }
512
+ ]);
458
513
  var OpenAIAdapter = class {
459
514
  vendor = openAIManifest.vendor;
460
515
  acceptedAuth = ["apiKey"];
@@ -472,7 +527,8 @@ var OpenAIAdapter = class {
472
527
  }
473
528
  stream(req) {
474
529
  return streamFromOpenAI(this.ensureClient(), req, {
475
- knownModels: openAIManifest.knownModels
530
+ knownModels: openAIManifest.knownModels,
531
+ vendorMetadataExtractor: OPENAI_METADATA_EXTRACTOR
476
532
  });
477
533
  }
478
534
  appendAssistantToolCall(history, toolCalls) {
@@ -704,6 +760,12 @@ var anthropicManifest = {
704
760
  };
705
761
 
706
762
  // src/adapters/anthropic/adapter.ts
763
+ var ANTHROPIC_METADATA_EXTRACTOR = makeMetadataExtractor([
764
+ { header: "request-id", key: "request_id" },
765
+ { header: "anthropic-ratelimit-requests-remaining", key: "requests_remaining", numeric: true },
766
+ { header: "anthropic-ratelimit-input-tokens-remaining", key: "input_tokens_remaining", numeric: true },
767
+ { header: "anthropic-ratelimit-output-tokens-remaining", key: "output_tokens_remaining", numeric: true }
768
+ ]);
707
769
  var AnthropicAdapter = class {
708
770
  vendor = anthropicManifest.vendor;
709
771
  acceptedAuth = ["apiKey"];
@@ -738,11 +800,20 @@ var AnthropicAdapter = class {
738
800
  if (tools) body.tools = tools;
739
801
  if (req.temperature !== void 0) body.temperature = req.temperature;
740
802
  let stream;
803
+ let responseHeaders;
741
804
  try {
742
- stream = await client.messages.create(
805
+ const apiPromise = client.messages.create(
743
806
  body,
744
807
  { signal: req.signal }
745
808
  );
809
+ const maybeWithResponse = apiPromise.withResponse;
810
+ if (typeof maybeWithResponse === "function") {
811
+ const result = await maybeWithResponse.call(apiPromise);
812
+ stream = result.data;
813
+ responseHeaders = result.response?.headers;
814
+ } else {
815
+ stream = await apiPromise;
816
+ }
746
817
  } catch (err) {
747
818
  if (isAbortError2(err)) {
748
819
  yield { type: "end", finishReason: "aborted" };
@@ -853,6 +924,12 @@ var AnthropicAdapter = class {
853
924
  ...usage ? { usage } : {},
854
925
  ...stopReason ? { rawReason: stopReason } : {}
855
926
  };
927
+ if (responseHeaders) {
928
+ const metadata = ANTHROPIC_METADATA_EXTRACTOR(responseHeaders);
929
+ if (Object.keys(metadata).length > 0) {
930
+ endEvent.vendorMetadata = metadata;
931
+ }
932
+ }
856
933
  yield endEvent;
857
934
  }
858
935
  appendAssistantToolCall(history, toolCalls) {
@@ -1645,6 +1722,9 @@ var vertexOpenAIManifest = {
1645
1722
  };
1646
1723
 
1647
1724
  // src/adapters/vertex-openai/adapter.ts
1725
+ var VERTEX_OPENAI_METADATA_EXTRACTOR = makeMetadataExtractor([
1726
+ { header: "x-request-id", key: "request_id" }
1727
+ ]);
1648
1728
  var VertexOpenAIAdapter = class {
1649
1729
  vendor = vertexOpenAIManifest.vendor;
1650
1730
  acceptedAuth = ["vertexServiceAccount"];
@@ -1697,7 +1777,8 @@ var VertexOpenAIAdapter = class {
1697
1777
  // endpoint predates `max_completion_tokens` and may not recognize it.
1698
1778
  // The model-id heuristic wouldn't catch a partner model named in the
1699
1779
  // OpenAI style (unlikely today, but defensive).
1700
- tokensParam: "max_tokens"
1780
+ tokensParam: "max_tokens",
1781
+ vendorMetadataExtractor: VERTEX_OPENAI_METADATA_EXTRACTOR
1701
1782
  });
1702
1783
  }
1703
1784
  appendAssistantToolCall(history, toolCalls) {
@@ -2155,6 +2236,21 @@ var BedrockAdapter = class {
2155
2236
  ...usage ? { usage } : {},
2156
2237
  ...stopReason ? { rawReason: stopReason } : {}
2157
2238
  };
2239
+ const httpHeaders = response.$metadata?.httpHeaders;
2240
+ if (httpHeaders) {
2241
+ const metadata = extractMetadata(
2242
+ [
2243
+ { header: "x-amzn-bedrock-invocation-latency", key: "invocation_latency_ms", numeric: true },
2244
+ { header: "x-amzn-bedrock-cache-read-input-token-count", key: "cache_read_input_tokens", numeric: true },
2245
+ { header: "x-amzn-bedrock-cache-write-input-token-count", key: "cache_write_input_tokens", numeric: true },
2246
+ { header: "x-amzn-bedrock-trace-id", key: "trace_id" }
2247
+ ],
2248
+ httpHeaders
2249
+ );
2250
+ if (Object.keys(metadata).length > 0) {
2251
+ endEvent.vendorMetadata = metadata;
2252
+ }
2253
+ }
2158
2254
  yield endEvent;
2159
2255
  }
2160
2256
  appendAssistantToolCall(history, toolCalls) {
@@ -2406,6 +2502,14 @@ var azureOpenAIManifest = {
2406
2502
  };
2407
2503
 
2408
2504
  // src/adapters/azure-openai/adapter.ts
2505
+ var AZURE_OPENAI_METADATA_EXTRACTOR = makeMetadataExtractor([
2506
+ { header: "x-ms-region", key: "region" },
2507
+ { header: "x-ms-deployment-name", key: "deployment" },
2508
+ { header: "apim-request-id", key: "request_id" },
2509
+ { header: "azureml-model-session", key: "model_session" },
2510
+ { header: "x-ratelimit-remaining-requests", key: "requests_remaining", numeric: true },
2511
+ { header: "x-ratelimit-remaining-tokens", key: "tokens_remaining", numeric: true }
2512
+ ]);
2409
2513
  var AzureOpenAIAdapter = class {
2410
2514
  vendor = azureOpenAIManifest.vendor;
2411
2515
  acceptedAuth = ["azureOpenAIApiKey"];
@@ -2440,7 +2544,8 @@ var AzureOpenAIAdapter = class {
2440
2544
  stream(req) {
2441
2545
  return streamFromOpenAI(this.ensureClient(), req, {
2442
2546
  knownModels: azureOpenAIManifest.knownModels,
2443
- tokensParam: "max_completion_tokens"
2547
+ tokensParam: "max_completion_tokens",
2548
+ vendorMetadataExtractor: AZURE_OPENAI_METADATA_EXTRACTOR
2444
2549
  });
2445
2550
  }
2446
2551
  appendAssistantToolCall(history, toolCalls) {
@@ -2565,6 +2670,12 @@ var groqManifest = {
2565
2670
  };
2566
2671
 
2567
2672
  // src/adapters/groq/adapter.ts
2673
+ var GROQ_METADATA_EXTRACTOR = makeMetadataExtractor([
2674
+ { header: "x-request-id", key: "request_id" },
2675
+ { header: "x-groq-region", key: "region" },
2676
+ { header: "x-ratelimit-remaining-requests", key: "requests_remaining", numeric: true },
2677
+ { header: "x-ratelimit-remaining-tokens", key: "tokens_remaining", numeric: true }
2678
+ ]);
2568
2679
  var GroqAdapter = class extends OpenAIAdapter {
2569
2680
  vendor = groqManifest.vendor;
2570
2681
  init(auth, client) {
@@ -2576,6 +2687,12 @@ var GroqAdapter = class extends OpenAIAdapter {
2576
2687
  client
2577
2688
  );
2578
2689
  }
2690
+ stream(req) {
2691
+ return streamFromOpenAI(this.ensureClient(), req, {
2692
+ knownModels: groqManifest.knownModels,
2693
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
2694
+ });
2695
+ }
2579
2696
  };
2580
2697
 
2581
2698
  // src/adapters/groq/factory.ts
@@ -2585,6 +2702,127 @@ var groqFactory = {
2585
2702
  create: () => new GroqAdapter()
2586
2703
  };
2587
2704
 
2705
+ // src/adapters/huggingface/manifest.ts
2706
+ var HF_KNOWN_MODELS = [
2707
+ {
2708
+ id: "meta-llama/Llama-3.3-70B-Instruct",
2709
+ displayName: "Llama 3.3 70B Instruct (HF Providers)",
2710
+ capabilities: {
2711
+ streaming: true,
2712
+ tools: true,
2713
+ vision: false,
2714
+ systemPrompt: true,
2715
+ maxContextTokens: 128e3
2716
+ }
2717
+ },
2718
+ {
2719
+ id: "meta-llama/Llama-3.1-8B-Instruct",
2720
+ displayName: "Llama 3.1 8B Instruct (HF Providers)",
2721
+ capabilities: {
2722
+ streaming: true,
2723
+ tools: true,
2724
+ vision: false,
2725
+ systemPrompt: true,
2726
+ maxContextTokens: 128e3
2727
+ }
2728
+ },
2729
+ {
2730
+ id: "Qwen/Qwen2.5-72B-Instruct",
2731
+ displayName: "Qwen 2.5 72B Instruct (HF Providers)",
2732
+ capabilities: {
2733
+ streaming: true,
2734
+ tools: true,
2735
+ vision: false,
2736
+ systemPrompt: true,
2737
+ maxContextTokens: 128e3
2738
+ }
2739
+ },
2740
+ {
2741
+ id: "mistralai/Mistral-7B-Instruct-v0.3",
2742
+ displayName: "Mistral 7B Instruct v0.3 (HF Providers)",
2743
+ capabilities: {
2744
+ streaming: true,
2745
+ tools: true,
2746
+ vision: false,
2747
+ systemPrompt: true,
2748
+ maxContextTokens: 32e3
2749
+ }
2750
+ },
2751
+ {
2752
+ id: "deepseek-ai/DeepSeek-V3",
2753
+ displayName: "DeepSeek V3 (HF Providers)",
2754
+ capabilities: {
2755
+ streaming: true,
2756
+ tools: true,
2757
+ vision: false,
2758
+ systemPrompt: true,
2759
+ maxContextTokens: 64e3
2760
+ }
2761
+ }
2762
+ ];
2763
+ var huggingfaceManifest = {
2764
+ vendor: "huggingface",
2765
+ displayName: "HuggingFace Inference Providers",
2766
+ authKinds: [
2767
+ {
2768
+ kind: "apiKey",
2769
+ displayName: "API Key",
2770
+ fields: [
2771
+ {
2772
+ name: "apiKey",
2773
+ label: "HuggingFace Token",
2774
+ type: "password",
2775
+ required: true,
2776
+ help: "Get a token from https://huggingface.co/settings/tokens (Read scope is sufficient). Inference Providers requires a credit balance \u2014 claim the free monthly tier or attach a payment method at https://huggingface.co/billing."
2777
+ },
2778
+ {
2779
+ name: "baseURL",
2780
+ label: "Base URL",
2781
+ type: "url",
2782
+ required: false,
2783
+ default: "https://router.huggingface.co/v1",
2784
+ help: "Override only for proxies. Default is the HF Providers router endpoint."
2785
+ }
2786
+ ]
2787
+ }
2788
+ ],
2789
+ knownModels: HF_KNOWN_MODELS,
2790
+ // The HF router exposes /v1/models with the same Bearer auth.
2791
+ supportsModelListing: true,
2792
+ docsUrl: "https://huggingface.co/docs/inference-providers"
2793
+ };
2794
+
2795
+ // src/adapters/huggingface/adapter.ts
2796
+ var HUGGINGFACE_METADATA_EXTRACTOR = makeMetadataExtractor([
2797
+ { header: "x-inference-provider", key: "provider" },
2798
+ { header: "x-request-id", key: "request_id" }
2799
+ ]);
2800
+ var HuggingfaceAdapter = class extends OpenAIAdapter {
2801
+ vendor = huggingfaceManifest.vendor;
2802
+ init(auth, client) {
2803
+ super.init(
2804
+ {
2805
+ ...auth,
2806
+ baseURL: auth.baseURL ?? "https://router.huggingface.co/v1"
2807
+ },
2808
+ client
2809
+ );
2810
+ }
2811
+ stream(req) {
2812
+ return streamFromOpenAI(this.ensureClient(), req, {
2813
+ knownModels: huggingfaceManifest.knownModels,
2814
+ vendorMetadataExtractor: HUGGINGFACE_METADATA_EXTRACTOR
2815
+ });
2816
+ }
2817
+ };
2818
+
2819
+ // src/adapters/huggingface/factory.ts
2820
+ var huggingfaceFactory = {
2821
+ vendor: huggingfaceManifest.vendor,
2822
+ manifest: huggingfaceManifest,
2823
+ create: () => new HuggingfaceAdapter()
2824
+ };
2825
+
2588
2826
  // src/adapters/index.ts
2589
2827
  registerAdapter(openAIFactory);
2590
2828
  registerAdapter(deepseekFactory);
@@ -2595,6 +2833,7 @@ registerAdapter(vertexOpenAIFactory);
2595
2833
  registerAdapter(bedrockFactory);
2596
2834
  registerAdapter(azureOpenAIFactory);
2597
2835
  registerAdapter(groqFactory);
2836
+ registerAdapter(huggingfaceFactory);
2598
2837
 
2599
2838
  // src/normalize-auth.ts
2600
2839
  function normalizeAuth(vendor, raw) {
@@ -2602,6 +2841,7 @@ function normalizeAuth(vendor, raw) {
2602
2841
  case "openai":
2603
2842
  case "anthropic":
2604
2843
  case "groq":
2844
+ case "huggingface":
2605
2845
  return requireApiKey(vendor, raw);
2606
2846
  case "deepseek":
2607
2847
  return {