@graphorin/provider 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.
Files changed (148) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +105 -0
  4. package/dist/adapters/index.d.ts +4 -0
  5. package/dist/adapters/index.js +6 -0
  6. package/dist/adapters/llamacpp-server.d.ts +69 -0
  7. package/dist/adapters/llamacpp-server.d.ts.map +1 -0
  8. package/dist/adapters/llamacpp-server.js +51 -0
  9. package/dist/adapters/llamacpp-server.js.map +1 -0
  10. package/dist/adapters/ollama.d.ts +43 -0
  11. package/dist/adapters/ollama.d.ts.map +1 -0
  12. package/dist/adapters/ollama.js +246 -0
  13. package/dist/adapters/ollama.js.map +1 -0
  14. package/dist/adapters/openai-compatible.d.ts +48 -0
  15. package/dist/adapters/openai-compatible.d.ts.map +1 -0
  16. package/dist/adapters/openai-compatible.js +28 -0
  17. package/dist/adapters/openai-compatible.js.map +1 -0
  18. package/dist/adapters/vercel.d.ts +135 -0
  19. package/dist/adapters/vercel.d.ts.map +1 -0
  20. package/dist/adapters/vercel.js +306 -0
  21. package/dist/adapters/vercel.js.map +1 -0
  22. package/dist/counters/anthropic.d.ts +36 -0
  23. package/dist/counters/anthropic.d.ts.map +1 -0
  24. package/dist/counters/anthropic.js +63 -0
  25. package/dist/counters/anthropic.js.map +1 -0
  26. package/dist/counters/bedrock.d.ts +27 -0
  27. package/dist/counters/bedrock.d.ts.map +1 -0
  28. package/dist/counters/bedrock.js +29 -0
  29. package/dist/counters/bedrock.js.map +1 -0
  30. package/dist/counters/dispatcher.d.ts +34 -0
  31. package/dist/counters/dispatcher.d.ts.map +1 -0
  32. package/dist/counters/dispatcher.js +78 -0
  33. package/dist/counters/dispatcher.js.map +1 -0
  34. package/dist/counters/global.d.ts +26 -0
  35. package/dist/counters/global.d.ts.map +1 -0
  36. package/dist/counters/global.js +31 -0
  37. package/dist/counters/global.js.map +1 -0
  38. package/dist/counters/google.d.ts +27 -0
  39. package/dist/counters/google.d.ts.map +1 -0
  40. package/dist/counters/google.js +29 -0
  41. package/dist/counters/google.js.map +1 -0
  42. package/dist/counters/heuristic.d.ts +37 -0
  43. package/dist/counters/heuristic.d.ts.map +1 -0
  44. package/dist/counters/heuristic.js +64 -0
  45. package/dist/counters/heuristic.js.map +1 -0
  46. package/dist/counters/index.d.ts +9 -0
  47. package/dist/counters/index.js +10 -0
  48. package/dist/counters/js-tiktoken.d.ts +63 -0
  49. package/dist/counters/js-tiktoken.d.ts.map +1 -0
  50. package/dist/counters/js-tiktoken.js +73 -0
  51. package/dist/counters/js-tiktoken.js.map +1 -0
  52. package/dist/counters/serialize.d.ts +37 -0
  53. package/dist/counters/serialize.d.ts.map +1 -0
  54. package/dist/counters/serialize.js +57 -0
  55. package/dist/counters/serialize.js.map +1 -0
  56. package/dist/errors/errors.d.ts +167 -0
  57. package/dist/errors/errors.d.ts.map +1 -0
  58. package/dist/errors/errors.js +182 -0
  59. package/dist/errors/errors.js.map +1 -0
  60. package/dist/errors/index.d.ts +2 -0
  61. package/dist/errors/index.js +3 -0
  62. package/dist/index.d.ts +69 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +70 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/internal/abort.js +34 -0
  67. package/dist/internal/abort.js.map +1 -0
  68. package/dist/internal/http.js +163 -0
  69. package/dist/internal/http.js.map +1 -0
  70. package/dist/internal/openai-shaped.js +308 -0
  71. package/dist/internal/openai-shaped.js.map +1 -0
  72. package/dist/internal/sse.js +104 -0
  73. package/dist/internal/sse.js.map +1 -0
  74. package/dist/internal/url-utils.js +24 -0
  75. package/dist/internal/url-utils.js.map +1 -0
  76. package/dist/middleware/compose.d.ts +85 -0
  77. package/dist/middleware/compose.d.ts.map +1 -0
  78. package/dist/middleware/compose.js +174 -0
  79. package/dist/middleware/compose.js.map +1 -0
  80. package/dist/middleware/index.d.ts +10 -0
  81. package/dist/middleware/index.js +11 -0
  82. package/dist/middleware/production-hook.d.ts +27 -0
  83. package/dist/middleware/production-hook.d.ts.map +1 -0
  84. package/dist/middleware/production-hook.js +21 -0
  85. package/dist/middleware/production-hook.js.map +1 -0
  86. package/dist/middleware/with-cost-limit.d.ts +46 -0
  87. package/dist/middleware/with-cost-limit.d.ts.map +1 -0
  88. package/dist/middleware/with-cost-limit.js +66 -0
  89. package/dist/middleware/with-cost-limit.js.map +1 -0
  90. package/dist/middleware/with-cost-tracking.d.ts +91 -0
  91. package/dist/middleware/with-cost-tracking.d.ts.map +1 -0
  92. package/dist/middleware/with-cost-tracking.js +95 -0
  93. package/dist/middleware/with-cost-tracking.js.map +1 -0
  94. package/dist/middleware/with-fallback.d.ts +25 -0
  95. package/dist/middleware/with-fallback.d.ts.map +1 -0
  96. package/dist/middleware/with-fallback.js +84 -0
  97. package/dist/middleware/with-fallback.js.map +1 -0
  98. package/dist/middleware/with-rate-limit.d.ts +36 -0
  99. package/dist/middleware/with-rate-limit.d.ts.map +1 -0
  100. package/dist/middleware/with-rate-limit.js +141 -0
  101. package/dist/middleware/with-rate-limit.js.map +1 -0
  102. package/dist/middleware/with-redaction.d.ts +60 -0
  103. package/dist/middleware/with-redaction.d.ts.map +1 -0
  104. package/dist/middleware/with-redaction.js +305 -0
  105. package/dist/middleware/with-redaction.js.map +1 -0
  106. package/dist/middleware/with-retry.d.ts +26 -0
  107. package/dist/middleware/with-retry.d.ts.map +1 -0
  108. package/dist/middleware/with-retry.js +157 -0
  109. package/dist/middleware/with-retry.js.map +1 -0
  110. package/dist/middleware/with-tracing.d.ts +21 -0
  111. package/dist/middleware/with-tracing.d.ts.map +1 -0
  112. package/dist/middleware/with-tracing.js +55 -0
  113. package/dist/middleware/with-tracing.js.map +1 -0
  114. package/dist/model-tier/classify.d.ts +34 -0
  115. package/dist/model-tier/classify.d.ts.map +1 -0
  116. package/dist/model-tier/classify.js +117 -0
  117. package/dist/model-tier/classify.js.map +1 -0
  118. package/dist/model-tier/index.d.ts +2 -0
  119. package/dist/model-tier/index.js +3 -0
  120. package/dist/provider.d.ts +51 -0
  121. package/dist/provider.d.ts.map +1 -0
  122. package/dist/provider.js +64 -0
  123. package/dist/provider.js.map +1 -0
  124. package/dist/reasoning/apply-policy.d.ts +22 -0
  125. package/dist/reasoning/apply-policy.d.ts.map +1 -0
  126. package/dist/reasoning/apply-policy.js +47 -0
  127. package/dist/reasoning/apply-policy.js.map +1 -0
  128. package/dist/reasoning/classify-contract.d.ts +41 -0
  129. package/dist/reasoning/classify-contract.d.ts.map +1 -0
  130. package/dist/reasoning/classify-contract.js +59 -0
  131. package/dist/reasoning/classify-contract.js.map +1 -0
  132. package/dist/reasoning/index.d.ts +4 -0
  133. package/dist/reasoning/index.js +5 -0
  134. package/dist/reasoning/retention.d.ts +39 -0
  135. package/dist/reasoning/retention.d.ts.map +1 -0
  136. package/dist/reasoning/retention.js +33 -0
  137. package/dist/reasoning/retention.js.map +1 -0
  138. package/dist/tool-examples.d.ts +14 -0
  139. package/dist/tool-examples.d.ts.map +1 -0
  140. package/dist/tool-examples.js +35 -0
  141. package/dist/tool-examples.js.map +1 -0
  142. package/dist/trust/classify-local-provider.d.ts +47 -0
  143. package/dist/trust/classify-local-provider.d.ts.map +1 -0
  144. package/dist/trust/classify-local-provider.js +164 -0
  145. package/dist/trust/classify-local-provider.js.map +1 -0
  146. package/dist/trust/index.d.ts +3 -0
  147. package/dist/trust/index.js +3 -0
  148. package/package.json +128 -0
@@ -0,0 +1,27 @@
1
+ import { Message, TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/bedrock.d.ts
4
+
5
+ /**
6
+ * Options for {@link BedrockAPICounter}.
7
+ *
8
+ * @stable
9
+ */
10
+ interface BedrockAPICounterOptions {
11
+ readonly modelId: string;
12
+ readonly id?: string;
13
+ }
14
+ /**
15
+ * @stable
16
+ */
17
+ declare class BedrockAPICounter implements TokenCounter {
18
+ #private;
19
+ readonly id: string;
20
+ readonly version: string;
21
+ constructor(options: BedrockAPICounterOptions);
22
+ count(messages: ReadonlyArray<Message>): Promise<number>;
23
+ countText(text: string): Promise<number>;
24
+ }
25
+ //#endregion
26
+ export { BedrockAPICounter, BedrockAPICounterOptions };
27
+ //# sourceMappingURL=bedrock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bedrock.d.ts","names":[],"sources":["../../src/counters/bedrock.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAmBiB,wBAAA;;;;;;;cAQJ,iBAAA,YAA6B;;;;uBAKnB;kBAML,cAAc,WAAW;2BAIhB"}
@@ -0,0 +1,29 @@
1
+ import { JsTiktokenCounter } from "./js-tiktoken.js";
2
+
3
+ //#region src/counters/bedrock.ts
4
+ /**
5
+ * @stable
6
+ */
7
+ var BedrockAPICounter = class {
8
+ id;
9
+ version;
10
+ #fallback;
11
+ constructor(options) {
12
+ this.#fallback = new JsTiktokenCounter({
13
+ encoding: "cl100k_base",
14
+ modelId: options.modelId
15
+ });
16
+ this.id = options.id ?? `bedrock-proxy@${options.modelId}`;
17
+ this.version = `bedrock-cl100k-proxy-${options.modelId}-v1`;
18
+ }
19
+ count(messages) {
20
+ return this.#fallback.count(messages);
21
+ }
22
+ countText(text) {
23
+ return this.#fallback.countText(text);
24
+ }
25
+ };
26
+
27
+ //#endregion
28
+ export { BedrockAPICounter };
29
+ //# sourceMappingURL=bedrock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bedrock.js","names":["#fallback"],"sources":["../../src/counters/bedrock.ts"],"sourcesContent":["/**\n * `BedrockAPICounter` — placeholder counter that delegates to\n * `JsTiktokenCounter('cl100k_base')` for now (Bedrock's native\n * `count_tokens` API requires AWS Signature v4; full integration\n * lands in Phase 16). The proxy is sufficient for v0.1 within the\n * documented ±5% accuracy envelope.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\nimport { JsTiktokenCounter } from './js-tiktoken.js';\n\n/**\n * Options for {@link BedrockAPICounter}.\n *\n * @stable\n */\nexport interface BedrockAPICounterOptions {\n readonly modelId: string;\n readonly id?: string;\n}\n\n/**\n * @stable\n */\nexport class BedrockAPICounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #fallback: JsTiktokenCounter;\n\n constructor(options: BedrockAPICounterOptions) {\n this.#fallback = new JsTiktokenCounter({ encoding: 'cl100k_base', modelId: options.modelId });\n this.id = options.id ?? `bedrock-proxy@${options.modelId}`;\n this.version = `bedrock-cl100k-proxy-${options.modelId}-v1`;\n }\n\n count(messages: ReadonlyArray<Message>): Promise<number> {\n return this.#fallback.count(messages);\n }\n\n countText(text: string): Promise<number> {\n return this.#fallback.countText(text);\n }\n}\n"],"mappings":";;;;;;AA2BA,IAAa,oBAAb,MAAuD;CACrD,AAAS;CACT,AAAS;CACT,CAASA;CAET,YAAY,SAAmC;AAC7C,QAAKA,WAAY,IAAI,kBAAkB;GAAE,UAAU;GAAe,SAAS,QAAQ;GAAS,CAAC;AAC7F,OAAK,KAAK,QAAQ,MAAM,iBAAiB,QAAQ;AACjD,OAAK,UAAU,wBAAwB,QAAQ,QAAQ;;CAGzD,MAAM,UAAmD;AACvD,SAAO,MAAKA,SAAU,MAAM,SAAS;;CAGvC,UAAU,MAA+B;AACvC,SAAO,MAAKA,SAAU,UAAU,KAAK"}
@@ -0,0 +1,34 @@
1
+ import { TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/dispatcher.d.ts
4
+
5
+ /**
6
+ * Options for {@link createDefaultCounter}.
7
+ *
8
+ * @stable
9
+ */
10
+ interface CreateDefaultCounterOptions {
11
+ /** Concrete model id (e.g. `'gpt-4o'`, `'claude-opus-4-7'`). */
12
+ readonly model: string;
13
+ /** Optional provider hint to short-circuit the regex matching. */
14
+ readonly provider?: string;
15
+ /** Optional Anthropic API key threaded through to {@link AnthropicAPICounter}. */
16
+ readonly anthropicApiKey?: string;
17
+ }
18
+ /**
19
+ * Build the recommended {@link TokenCounter} for the given
20
+ * `(provider, model)` pair. The dispatch table:
21
+ *
22
+ * - Anthropic Claude → `AnthropicAPICounter` (native if `apiKey` set,
23
+ * otherwise `cl100k_base` proxy).
24
+ * - OpenAI / OpenAI-compatible → `JsTiktokenCounter('cl100k_base')`.
25
+ * - Google Gemini → `GoogleAPICounter` (cl100k_base proxy in v0.1).
26
+ * - Bedrock Claude → `BedrockAPICounter` (cl100k_base proxy in v0.1).
27
+ * - Ollama / unknown → `HeuristicCounter` with one WARN per process.
28
+ *
29
+ * @stable
30
+ */
31
+ declare function createDefaultCounter(options: CreateDefaultCounterOptions): TokenCounter;
32
+ //#endregion
33
+ export { CreateDefaultCounterOptions, createDefaultCounter };
34
+ //# sourceMappingURL=dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.d.ts","names":[],"sources":["../../src/counters/dispatcher.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAuBiB,2BAAA;;;;;;;;;;;;;;;;;;;;;iBAuDD,oBAAA,UAA8B,8BAA8B"}
@@ -0,0 +1,78 @@
1
+ import { JsTiktokenCounter } from "./js-tiktoken.js";
2
+ import { AnthropicAPICounter } from "./anthropic.js";
3
+ import { BedrockAPICounter } from "./bedrock.js";
4
+ import { GoogleAPICounter } from "./google.js";
5
+ import { HeuristicCounter } from "./heuristic.js";
6
+
7
+ //#region src/counters/dispatcher.ts
8
+ /**
9
+ * Detect the provider family for a model id. Exposed for downstream
10
+ * consumers (the model-tier classifier; future per-tool counters) so
11
+ * provider-family detection is centralised here.
12
+ *
13
+ * @stable
14
+ */
15
+ function detectProviderFamily(args) {
16
+ const provider = args.provider?.toLowerCase();
17
+ const modelLower = args.model.toLowerCase();
18
+ if (provider === "anthropic" || modelLower.startsWith("claude") || modelLower.startsWith("anthropic.claude")) {
19
+ if (provider === "bedrock" || modelLower.startsWith("anthropic.claude")) return "bedrock";
20
+ return "anthropic";
21
+ }
22
+ if (provider === "google" || modelLower.startsWith("gemini")) return "google";
23
+ if (provider === "bedrock") return "bedrock";
24
+ if (provider === "openai" || provider === "azure-openai" || /^(gpt-|o1|o3|o4)/.test(modelLower)) return "openai";
25
+ if (provider === "ollama" || modelLower.startsWith("ollama-")) return "ollama";
26
+ if (provider === "openai-compatible" || provider === "lmstudio" || provider === "vllm") return "openai-compatible";
27
+ return "unknown";
28
+ }
29
+ /**
30
+ * Build the recommended {@link TokenCounter} for the given
31
+ * `(provider, model)` pair. The dispatch table:
32
+ *
33
+ * - Anthropic Claude → `AnthropicAPICounter` (native if `apiKey` set,
34
+ * otherwise `cl100k_base` proxy).
35
+ * - OpenAI / OpenAI-compatible → `JsTiktokenCounter('cl100k_base')`.
36
+ * - Google Gemini → `GoogleAPICounter` (cl100k_base proxy in v0.1).
37
+ * - Bedrock Claude → `BedrockAPICounter` (cl100k_base proxy in v0.1).
38
+ * - Ollama / unknown → `HeuristicCounter` with one WARN per process.
39
+ *
40
+ * @stable
41
+ */
42
+ function createDefaultCounter(options) {
43
+ switch (detectProviderFamily({
44
+ model: options.model,
45
+ ...options.provider !== void 0 ? { provider: options.provider } : {}
46
+ })) {
47
+ case "anthropic": return new AnthropicAPICounter({
48
+ modelId: options.model,
49
+ ...options.anthropicApiKey !== void 0 ? { apiKey: options.anthropicApiKey } : {}
50
+ });
51
+ case "bedrock": return new BedrockAPICounter({ modelId: options.model });
52
+ case "google": return new GoogleAPICounter({ modelId: options.model });
53
+ case "openai":
54
+ case "openai-compatible": return new JsTiktokenCounter({
55
+ encoding: defaultOpenAiEncoding(options.model),
56
+ modelId: options.model
57
+ });
58
+ default: return new HeuristicCounter({ modelId: options.model });
59
+ }
60
+ }
61
+ /**
62
+ * PS-20: pick the fallback tiktoken encoding by model family. `js-tiktoken`'s
63
+ * `encodingForModel` handles ids it recognises; this is the explicit fallback
64
+ * for ids it does not (the 2025/2026 families). gpt-4o / gpt-4.1 / gpt-5+ and
65
+ * the o-series reasoning models use `o200k_base`; legacy gpt-4 / gpt-3.5 stay
66
+ * on `cl100k_base`.
67
+ */
68
+ function defaultOpenAiEncoding(model) {
69
+ if (model === void 0) return "cl100k_base";
70
+ const id = model.toLowerCase();
71
+ const sep = Math.max(id.lastIndexOf("/"), id.lastIndexOf(":"));
72
+ const bare = sep === -1 ? id : id.slice(sep + 1);
73
+ return /^(?:gpt-4o|gpt-4\.1|gpt-[5-9]|gpt-\d{2}|o[1-9])/.test(bare) ? "o200k_base" : "cl100k_base";
74
+ }
75
+
76
+ //#endregion
77
+ export { createDefaultCounter };
78
+ //# sourceMappingURL=dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.js","names":[],"sources":["../../src/counters/dispatcher.ts"],"sourcesContent":["/**\n * `createDefaultCounter` — model-id classifier that returns the\n * recommended {@link TokenCounter} for a given `(provider, model)`\n * pair. The same classifier is reused by the model-tier dispatcher\n * (`@graphorin/provider/model-tier`) so provider-family detection\n * lives in exactly one place.\n *\n * @packageDocumentation\n */\n\nimport type { TokenCounter } from '@graphorin/core';\n\nimport { AnthropicAPICounter } from './anthropic.js';\nimport { BedrockAPICounter } from './bedrock.js';\nimport { GoogleAPICounter } from './google.js';\nimport { HeuristicCounter } from './heuristic.js';\nimport { JsTiktokenCounter } from './js-tiktoken.js';\n\n/**\n * Options for {@link createDefaultCounter}.\n *\n * @stable\n */\nexport interface CreateDefaultCounterOptions {\n /** Concrete model id (e.g. `'gpt-4o'`, `'claude-opus-4-7'`). */\n readonly model: string;\n /** Optional provider hint to short-circuit the regex matching. */\n readonly provider?: string;\n /** Optional Anthropic API key threaded through to {@link AnthropicAPICounter}. */\n readonly anthropicApiKey?: string;\n}\n\n/**\n * Detect the provider family for a model id. Exposed for downstream\n * consumers (the model-tier classifier; future per-tool counters) so\n * provider-family detection is centralised here.\n *\n * @stable\n */\nexport function detectProviderFamily(args: {\n readonly model: string;\n readonly provider?: string;\n}): 'anthropic' | 'openai' | 'google' | 'bedrock' | 'ollama' | 'openai-compatible' | 'unknown' {\n const provider = args.provider?.toLowerCase();\n const modelLower = args.model.toLowerCase();\n if (\n provider === 'anthropic' ||\n modelLower.startsWith('claude') ||\n modelLower.startsWith('anthropic.claude')\n ) {\n if (provider === 'bedrock' || modelLower.startsWith('anthropic.claude')) return 'bedrock';\n return 'anthropic';\n }\n if (provider === 'google' || modelLower.startsWith('gemini')) return 'google';\n if (provider === 'bedrock') return 'bedrock';\n if (provider === 'openai' || provider === 'azure-openai' || /^(gpt-|o1|o3|o4)/.test(modelLower)) {\n return 'openai';\n }\n if (provider === 'ollama' || modelLower.startsWith('ollama-')) return 'ollama';\n if (provider === 'openai-compatible' || provider === 'lmstudio' || provider === 'vllm') {\n return 'openai-compatible';\n }\n return 'unknown';\n}\n\n/**\n * Build the recommended {@link TokenCounter} for the given\n * `(provider, model)` pair. The dispatch table:\n *\n * - Anthropic Claude → `AnthropicAPICounter` (native if `apiKey` set,\n * otherwise `cl100k_base` proxy).\n * - OpenAI / OpenAI-compatible → `JsTiktokenCounter('cl100k_base')`.\n * - Google Gemini → `GoogleAPICounter` (cl100k_base proxy in v0.1).\n * - Bedrock Claude → `BedrockAPICounter` (cl100k_base proxy in v0.1).\n * - Ollama / unknown → `HeuristicCounter` with one WARN per process.\n *\n * @stable\n */\nexport function createDefaultCounter(options: CreateDefaultCounterOptions): TokenCounter {\n const family = detectProviderFamily({\n model: options.model,\n ...(options.provider !== undefined ? { provider: options.provider } : {}),\n });\n switch (family) {\n case 'anthropic':\n return new AnthropicAPICounter({\n modelId: options.model,\n ...(options.anthropicApiKey !== undefined ? { apiKey: options.anthropicApiKey } : {}),\n });\n case 'bedrock':\n return new BedrockAPICounter({ modelId: options.model });\n case 'google':\n return new GoogleAPICounter({ modelId: options.model });\n case 'openai':\n case 'openai-compatible':\n return new JsTiktokenCounter({\n encoding: defaultOpenAiEncoding(options.model),\n modelId: options.model,\n });\n default:\n return new HeuristicCounter({ modelId: options.model });\n }\n}\n\n/**\n * PS-20: pick the fallback tiktoken encoding by model family. `js-tiktoken`'s\n * `encodingForModel` handles ids it recognises; this is the explicit fallback\n * for ids it does not (the 2025/2026 families). gpt-4o / gpt-4.1 / gpt-5+ and\n * the o-series reasoning models use `o200k_base`; legacy gpt-4 / gpt-3.5 stay\n * on `cl100k_base`.\n */\nexport function defaultOpenAiEncoding(model: string | undefined): 'o200k_base' | 'cl100k_base' {\n if (model === undefined) return 'cl100k_base';\n const id = model.toLowerCase();\n const sep = Math.max(id.lastIndexOf('/'), id.lastIndexOf(':'));\n const bare = sep === -1 ? id : id.slice(sep + 1);\n return /^(?:gpt-4o|gpt-4\\.1|gpt-[5-9]|gpt-\\d{2}|o[1-9])/.test(bare)\n ? 'o200k_base'\n : 'cl100k_base';\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,SAAgB,qBAAqB,MAG0D;CAC7F,MAAM,WAAW,KAAK,UAAU,aAAa;CAC7C,MAAM,aAAa,KAAK,MAAM,aAAa;AAC3C,KACE,aAAa,eACb,WAAW,WAAW,SAAS,IAC/B,WAAW,WAAW,mBAAmB,EACzC;AACA,MAAI,aAAa,aAAa,WAAW,WAAW,mBAAmB,CAAE,QAAO;AAChF,SAAO;;AAET,KAAI,aAAa,YAAY,WAAW,WAAW,SAAS,CAAE,QAAO;AACrE,KAAI,aAAa,UAAW,QAAO;AACnC,KAAI,aAAa,YAAY,aAAa,kBAAkB,mBAAmB,KAAK,WAAW,CAC7F,QAAO;AAET,KAAI,aAAa,YAAY,WAAW,WAAW,UAAU,CAAE,QAAO;AACtE,KAAI,aAAa,uBAAuB,aAAa,cAAc,aAAa,OAC9E,QAAO;AAET,QAAO;;;;;;;;;;;;;;;AAgBT,SAAgB,qBAAqB,SAAoD;AAKvF,SAJe,qBAAqB;EAClC,OAAO,QAAQ;EACf,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EACzE,CAAC,EACF;EACE,KAAK,YACH,QAAO,IAAI,oBAAoB;GAC7B,SAAS,QAAQ;GACjB,GAAI,QAAQ,oBAAoB,SAAY,EAAE,QAAQ,QAAQ,iBAAiB,GAAG,EAAE;GACrF,CAAC;EACJ,KAAK,UACH,QAAO,IAAI,kBAAkB,EAAE,SAAS,QAAQ,OAAO,CAAC;EAC1D,KAAK,SACH,QAAO,IAAI,iBAAiB,EAAE,SAAS,QAAQ,OAAO,CAAC;EACzD,KAAK;EACL,KAAK,oBACH,QAAO,IAAI,kBAAkB;GAC3B,UAAU,sBAAsB,QAAQ,MAAM;GAC9C,SAAS,QAAQ;GAClB,CAAC;EACJ,QACE,QAAO,IAAI,iBAAiB,EAAE,SAAS,QAAQ,OAAO,CAAC;;;;;;;;;;AAW7D,SAAgB,sBAAsB,OAAyD;AAC7F,KAAI,UAAU,OAAW,QAAO;CAChC,MAAM,KAAK,MAAM,aAAa;CAC9B,MAAM,MAAM,KAAK,IAAI,GAAG,YAAY,IAAI,EAAE,GAAG,YAAY,IAAI,CAAC;CAC9D,MAAM,OAAO,QAAQ,KAAK,KAAK,GAAG,MAAM,MAAM,EAAE;AAChD,QAAO,kDAAkD,KAAK,KAAK,GAC/D,eACA"}
@@ -0,0 +1,26 @@
1
+ import { TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/global.d.ts
4
+
5
+ /**
6
+ * Set the process-global counter. Called once at startup by user
7
+ * code; passing `null` clears the slot.
8
+ *
9
+ * @stable
10
+ */
11
+ declare function setGlobalTokenCounter(counter: TokenCounter | null): void;
12
+ /**
13
+ * Read the process-global counter, or `null` if unset.
14
+ *
15
+ * @stable
16
+ */
17
+ declare function getGlobalTokenCounter(): TokenCounter | null;
18
+ /**
19
+ * Test-only hook that resets the global slot to `null` between cases.
20
+ *
21
+ * @internal
22
+ */
23
+ declare function __resetGlobalTokenCounter(): void;
24
+ //#endregion
25
+ export { __resetGlobalTokenCounter, getGlobalTokenCounter, setGlobalTokenCounter };
26
+ //# sourceMappingURL=global.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global.d.ts","names":[],"sources":["../../src/counters/global.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;iBAkBgB,qBAAA,UAA+B;;;;;;iBAS/B,qBAAA,CAAA,GAAyB;;;;;;iBASzB,yBAAA,CAAA"}
@@ -0,0 +1,31 @@
1
+ //#region src/counters/global.ts
2
+ let globalCounter = null;
3
+ /**
4
+ * Set the process-global counter. Called once at startup by user
5
+ * code; passing `null` clears the slot.
6
+ *
7
+ * @stable
8
+ */
9
+ function setGlobalTokenCounter(counter) {
10
+ globalCounter = counter;
11
+ }
12
+ /**
13
+ * Read the process-global counter, or `null` if unset.
14
+ *
15
+ * @stable
16
+ */
17
+ function getGlobalTokenCounter() {
18
+ return globalCounter;
19
+ }
20
+ /**
21
+ * Test-only hook that resets the global slot to `null` between cases.
22
+ *
23
+ * @internal
24
+ */
25
+ function __resetGlobalTokenCounter() {
26
+ globalCounter = null;
27
+ }
28
+
29
+ //#endregion
30
+ export { __resetGlobalTokenCounter, getGlobalTokenCounter, setGlobalTokenCounter };
31
+ //# sourceMappingURL=global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global.js","names":["globalCounter: TokenCounter | null"],"sources":["../../src/counters/global.ts"],"sourcesContent":["/**\n * Process-global token counter slot. Components (e.g. the\n * ContextEngine in Phase 10) read the global counter when no\n * per-provider counter is wired explicitly.\n *\n * @packageDocumentation\n */\n\nimport type { TokenCounter } from '@graphorin/core';\n\nlet globalCounter: TokenCounter | null = null;\n\n/**\n * Set the process-global counter. Called once at startup by user\n * code; passing `null` clears the slot.\n *\n * @stable\n */\nexport function setGlobalTokenCounter(counter: TokenCounter | null): void {\n globalCounter = counter;\n}\n\n/**\n * Read the process-global counter, or `null` if unset.\n *\n * @stable\n */\nexport function getGlobalTokenCounter(): TokenCounter | null {\n return globalCounter;\n}\n\n/**\n * Test-only hook that resets the global slot to `null` between cases.\n *\n * @internal\n */\nexport function __resetGlobalTokenCounter(): void {\n globalCounter = null;\n}\n"],"mappings":";AAUA,IAAIA,gBAAqC;;;;;;;AAQzC,SAAgB,sBAAsB,SAAoC;AACxE,iBAAgB;;;;;;;AAQlB,SAAgB,wBAA6C;AAC3D,QAAO;;;;;;;AAQT,SAAgB,4BAAkC;AAChD,iBAAgB"}
@@ -0,0 +1,27 @@
1
+ import { Message, TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/google.d.ts
4
+
5
+ /**
6
+ * Options for {@link GoogleAPICounter}.
7
+ *
8
+ * @stable
9
+ */
10
+ interface GoogleAPICounterOptions {
11
+ readonly modelId: string;
12
+ readonly id?: string;
13
+ }
14
+ /**
15
+ * @stable
16
+ */
17
+ declare class GoogleAPICounter implements TokenCounter {
18
+ #private;
19
+ readonly id: string;
20
+ readonly version: string;
21
+ constructor(options: GoogleAPICounterOptions);
22
+ count(messages: ReadonlyArray<Message>): Promise<number>;
23
+ countText(text: string): Promise<number>;
24
+ }
25
+ //#endregion
26
+ export { GoogleAPICounter, GoogleAPICounterOptions };
27
+ //# sourceMappingURL=google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.d.ts","names":[],"sources":["../../src/counters/google.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAmBiB,uBAAA;;;;;;;cAQJ,gBAAA,YAA4B;;;;uBAKlB;kBAML,cAAc,WAAW;2BAIhB"}
@@ -0,0 +1,29 @@
1
+ import { JsTiktokenCounter } from "./js-tiktoken.js";
2
+
3
+ //#region src/counters/google.ts
4
+ /**
5
+ * @stable
6
+ */
7
+ var GoogleAPICounter = class {
8
+ id;
9
+ version;
10
+ #fallback;
11
+ constructor(options) {
12
+ this.#fallback = new JsTiktokenCounter({
13
+ encoding: "cl100k_base",
14
+ modelId: options.modelId
15
+ });
16
+ this.id = options.id ?? `google-proxy@${options.modelId}`;
17
+ this.version = `google-cl100k-proxy-${options.modelId}-v1`;
18
+ }
19
+ count(messages) {
20
+ return this.#fallback.count(messages);
21
+ }
22
+ countText(text) {
23
+ return this.#fallback.countText(text);
24
+ }
25
+ };
26
+
27
+ //#endregion
28
+ export { GoogleAPICounter };
29
+ //# sourceMappingURL=google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.js","names":["#fallback"],"sources":["../../src/counters/google.ts"],"sourcesContent":["/**\n * `GoogleAPICounter` — placeholder counter that delegates to\n * `JsTiktokenCounter('cl100k_base')` for now. The full Google `:countTokens`\n * implementation lands in Phase 16 alongside the optional pricing\n * refresher; for v0.1 the proxy is sufficient (verified within ±5% on\n * the standard fixture corpus per the existing DEC-131 acceptance).\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\nimport { JsTiktokenCounter } from './js-tiktoken.js';\n\n/**\n * Options for {@link GoogleAPICounter}.\n *\n * @stable\n */\nexport interface GoogleAPICounterOptions {\n readonly modelId: string;\n readonly id?: string;\n}\n\n/**\n * @stable\n */\nexport class GoogleAPICounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #fallback: JsTiktokenCounter;\n\n constructor(options: GoogleAPICounterOptions) {\n this.#fallback = new JsTiktokenCounter({ encoding: 'cl100k_base', modelId: options.modelId });\n this.id = options.id ?? `google-proxy@${options.modelId}`;\n this.version = `google-cl100k-proxy-${options.modelId}-v1`;\n }\n\n count(messages: ReadonlyArray<Message>): Promise<number> {\n return this.#fallback.count(messages);\n }\n\n countText(text: string): Promise<number> {\n return this.#fallback.countText(text);\n }\n}\n"],"mappings":";;;;;;AA2BA,IAAa,mBAAb,MAAsD;CACpD,AAAS;CACT,AAAS;CACT,CAASA;CAET,YAAY,SAAkC;AAC5C,QAAKA,WAAY,IAAI,kBAAkB;GAAE,UAAU;GAAe,SAAS,QAAQ;GAAS,CAAC;AAC7F,OAAK,KAAK,QAAQ,MAAM,gBAAgB,QAAQ;AAChD,OAAK,UAAU,uBAAuB,QAAQ,QAAQ;;CAGxD,MAAM,UAAmD;AACvD,SAAO,MAAKA,SAAU,MAAM,SAAS;;CAGvC,UAAU,MAA+B;AACvC,SAAO,MAAKA,SAAU,UAAU,KAAK"}
@@ -0,0 +1,37 @@
1
+ import { Message, TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/heuristic.d.ts
4
+
5
+ /**
6
+ * Options for {@link HeuristicCounter}.
7
+ *
8
+ * @stable
9
+ */
10
+ interface HeuristicCounterOptions {
11
+ /** Average characters per token. Defaults to `4` (latin-1 estimate). */
12
+ readonly charsPerToken?: number;
13
+ /** Identifier carried on the produced counter. */
14
+ readonly id?: string;
15
+ /** Optional model hint surfaced in the WARN message. */
16
+ readonly modelId?: string;
17
+ /** Optional sink. Defaults to `console.warn`. */
18
+ readonly logger?: (message: string, meta?: object) => void;
19
+ }
20
+ /**
21
+ * Counter that estimates tokens via simple character division. The
22
+ * estimator is deterministic and side-effect-free apart from the
23
+ * one-time WARN.
24
+ *
25
+ * @stable
26
+ */
27
+ declare class HeuristicCounter implements TokenCounter {
28
+ #private;
29
+ readonly id: string;
30
+ readonly version: string;
31
+ constructor(options?: HeuristicCounterOptions);
32
+ count(messages: ReadonlyArray<Message>): Promise<number>;
33
+ countText(text: string): Promise<number>;
34
+ }
35
+ //#endregion
36
+ export { HeuristicCounter, HeuristicCounterOptions };
37
+ //# sourceMappingURL=heuristic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristic.d.ts","names":[],"sources":["../../src/counters/heuristic.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAkBiB,uBAAA;;;;;;;;;;;;;;;;;cAoBJ,gBAAA,YAA4B;;;;wBAQlB;kBAYC,cAAc,WAAW;2BAUhB"}
@@ -0,0 +1,64 @@
1
+ import { serialiseMessageForCount, serializedToString } from "./serialize.js";
2
+
3
+ //#region src/counters/heuristic.ts
4
+ const WARNED = /* @__PURE__ */ new Set();
5
+ /**
6
+ * Counter that estimates tokens via simple character division. The
7
+ * estimator is deterministic and side-effect-free apart from the
8
+ * one-time WARN.
9
+ *
10
+ * @stable
11
+ */
12
+ var HeuristicCounter = class {
13
+ id;
14
+ version;
15
+ #charsPerToken;
16
+ #modelId;
17
+ #logger;
18
+ #warnedOnce = false;
19
+ constructor(options = {}) {
20
+ const charsPerToken = options.charsPerToken ?? 4;
21
+ if (!(charsPerToken > 0)) throw new RangeError("HeuristicCounter: charsPerToken must be > 0.");
22
+ this.#charsPerToken = charsPerToken;
23
+ this.id = options.id ?? `heuristic@${charsPerToken}cpt`;
24
+ this.version = `heuristic-${charsPerToken}-cpt-v1`;
25
+ if (options.modelId !== void 0) this.#modelId = options.modelId;
26
+ this.#logger = options.logger ?? defaultLogger;
27
+ }
28
+ async count(messages) {
29
+ this.#warnIfNeeded();
30
+ let total = 0;
31
+ for (const msg of messages) {
32
+ const serialised = serialiseMessageForCount(msg);
33
+ total += this.#estimate(serializedToString(serialised));
34
+ }
35
+ return total;
36
+ }
37
+ async countText(text) {
38
+ this.#warnIfNeeded();
39
+ return this.#estimate(text);
40
+ }
41
+ #estimate(text) {
42
+ if (text.length === 0) return 0;
43
+ return Math.ceil(text.length / this.#charsPerToken);
44
+ }
45
+ #warnIfNeeded() {
46
+ if (this.#warnedOnce) return;
47
+ const key = `${this.id}::${this.#modelId ?? "unknown"}`;
48
+ if (WARNED.has(key)) {
49
+ this.#warnedOnce = true;
50
+ return;
51
+ }
52
+ WARNED.add(key);
53
+ this.#warnedOnce = true;
54
+ this.#logger(`[graphorin/provider] heuristic token counter active (${this.id}); accuracy is approximate (~chars/${this.#charsPerToken}).`, this.#modelId !== void 0 ? { modelId: this.#modelId } : {});
55
+ }
56
+ };
57
+ function defaultLogger(message, meta) {
58
+ if (meta !== void 0) console.warn(message, meta);
59
+ else console.warn(message);
60
+ }
61
+
62
+ //#endregion
63
+ export { HeuristicCounter };
64
+ //# sourceMappingURL=heuristic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristic.js","names":["#charsPerToken","#modelId","#logger","#warnIfNeeded","#estimate","#warnedOnce"],"sources":["../../src/counters/heuristic.ts"],"sourcesContent":["/**\n * Conservative `chars / N` token-count estimator. Used as the fallback\n * for unknown providers; emits exactly one WARN per unique\n * `(provider, model)` tuple per process so operators are nudged\n * toward a precise counter.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\nimport { serialiseMessageForCount, serializedToString } from './serialize.js';\n\n/**\n * Options for {@link HeuristicCounter}.\n *\n * @stable\n */\nexport interface HeuristicCounterOptions {\n /** Average characters per token. Defaults to `4` (latin-1 estimate). */\n readonly charsPerToken?: number;\n /** Identifier carried on the produced counter. */\n readonly id?: string;\n /** Optional model hint surfaced in the WARN message. */\n readonly modelId?: string;\n /** Optional sink. Defaults to `console.warn`. */\n readonly logger?: (message: string, meta?: object) => void;\n}\n\nconst WARNED = new Set<string>();\n\n/**\n * Counter that estimates tokens via simple character division. The\n * estimator is deterministic and side-effect-free apart from the\n * one-time WARN.\n *\n * @stable\n */\nexport class HeuristicCounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #charsPerToken: number;\n readonly #modelId?: string;\n readonly #logger: (message: string, meta?: object) => void;\n #warnedOnce = false;\n\n constructor(options: HeuristicCounterOptions = {}) {\n const charsPerToken = options.charsPerToken ?? 4;\n if (!(charsPerToken > 0)) {\n throw new RangeError('HeuristicCounter: charsPerToken must be > 0.');\n }\n this.#charsPerToken = charsPerToken;\n this.id = options.id ?? `heuristic@${charsPerToken}cpt`;\n this.version = `heuristic-${charsPerToken}-cpt-v1`;\n if (options.modelId !== undefined) this.#modelId = options.modelId;\n this.#logger = options.logger ?? defaultLogger;\n }\n\n async count(messages: ReadonlyArray<Message>): Promise<number> {\n this.#warnIfNeeded();\n let total = 0;\n for (const msg of messages) {\n const serialised = serialiseMessageForCount(msg);\n total += this.#estimate(serializedToString(serialised));\n }\n return total;\n }\n\n async countText(text: string): Promise<number> {\n this.#warnIfNeeded();\n return this.#estimate(text);\n }\n\n #estimate(text: string): number {\n if (text.length === 0) return 0;\n return Math.ceil(text.length / this.#charsPerToken);\n }\n\n #warnIfNeeded(): void {\n if (this.#warnedOnce) return;\n const key = `${this.id}::${this.#modelId ?? 'unknown'}`;\n if (WARNED.has(key)) {\n this.#warnedOnce = true;\n return;\n }\n WARNED.add(key);\n this.#warnedOnce = true;\n this.#logger(\n `[graphorin/provider] heuristic token counter active (${this.id}); accuracy is approximate (~chars/${this.#charsPerToken}).`,\n this.#modelId !== undefined ? { modelId: this.#modelId } : {},\n );\n }\n}\n\nfunction defaultLogger(message: string, meta?: object): void {\n if (meta !== undefined) console.warn(message, meta);\n else console.warn(message);\n}\n\n/**\n * Test-only hook that resets the per-process WARN cache.\n *\n * @internal\n */\nexport function __resetHeuristicWarnCache(): void {\n WARNED.clear();\n}\n"],"mappings":";;;AA6BA,MAAM,yBAAS,IAAI,KAAa;;;;;;;;AAShC,IAAa,mBAAb,MAAsD;CACpD,AAAS;CACT,AAAS;CACT,CAASA;CACT,CAASC;CACT,CAASC;CACT,cAAc;CAEd,YAAY,UAAmC,EAAE,EAAE;EACjD,MAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,MAAI,EAAE,gBAAgB,GACpB,OAAM,IAAI,WAAW,+CAA+C;AAEtE,QAAKF,gBAAiB;AACtB,OAAK,KAAK,QAAQ,MAAM,aAAa,cAAc;AACnD,OAAK,UAAU,aAAa,cAAc;AAC1C,MAAI,QAAQ,YAAY,OAAW,OAAKC,UAAW,QAAQ;AAC3D,QAAKC,SAAU,QAAQ,UAAU;;CAGnC,MAAM,MAAM,UAAmD;AAC7D,QAAKC,cAAe;EACpB,IAAI,QAAQ;AACZ,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,aAAa,yBAAyB,IAAI;AAChD,YAAS,MAAKC,SAAU,mBAAmB,WAAW,CAAC;;AAEzD,SAAO;;CAGT,MAAM,UAAU,MAA+B;AAC7C,QAAKD,cAAe;AACpB,SAAO,MAAKC,SAAU,KAAK;;CAG7B,UAAU,MAAsB;AAC9B,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,KAAK,KAAK,KAAK,SAAS,MAAKJ,cAAe;;CAGrD,gBAAsB;AACpB,MAAI,MAAKK,WAAa;EACtB,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,MAAKJ,WAAY;AAC5C,MAAI,OAAO,IAAI,IAAI,EAAE;AACnB,SAAKI,aAAc;AACnB;;AAEF,SAAO,IAAI,IAAI;AACf,QAAKA,aAAc;AACnB,QAAKH,OACH,wDAAwD,KAAK,GAAG,qCAAqC,MAAKF,cAAe,KACzH,MAAKC,YAAa,SAAY,EAAE,SAAS,MAAKA,SAAU,GAAG,EAAE,CAC9D;;;AAIL,SAAS,cAAc,SAAiB,MAAqB;AAC3D,KAAI,SAAS,OAAW,SAAQ,KAAK,SAAS,KAAK;KAC9C,SAAQ,KAAK,QAAQ"}
@@ -0,0 +1,9 @@
1
+ import { AnthropicAPICounter, AnthropicAPICounterOptions } from "./anthropic.js";
2
+ import { BedrockAPICounter, BedrockAPICounterOptions } from "./bedrock.js";
3
+ import { CreateDefaultCounterOptions, createDefaultCounter } from "./dispatcher.js";
4
+ import { __resetGlobalTokenCounter, getGlobalTokenCounter, setGlobalTokenCounter } from "./global.js";
5
+ import { GoogleAPICounter, GoogleAPICounterOptions } from "./google.js";
6
+ import { HeuristicCounter, HeuristicCounterOptions } from "./heuristic.js";
7
+ import { JsTiktokenCounter, JsTiktokenCounterOptions, __resetTiktokenCache } from "./js-tiktoken.js";
8
+ import { SerializedMessage, serialiseMessageForCount, serializedToString } from "./serialize.js";
9
+ export { AnthropicAPICounter, type AnthropicAPICounterOptions, BedrockAPICounter, type BedrockAPICounterOptions, type CreateDefaultCounterOptions, GoogleAPICounter, type GoogleAPICounterOptions, HeuristicCounter, type HeuristicCounterOptions, JsTiktokenCounter, type JsTiktokenCounterOptions, type SerializedMessage, __resetGlobalTokenCounter, __resetTiktokenCache, createDefaultCounter, getGlobalTokenCounter, serialiseMessageForCount, serializedToString, setGlobalTokenCounter };
@@ -0,0 +1,10 @@
1
+ import { serialiseMessageForCount, serializedToString } from "./serialize.js";
2
+ import { JsTiktokenCounter, __resetTiktokenCache } from "./js-tiktoken.js";
3
+ import { AnthropicAPICounter } from "./anthropic.js";
4
+ import { BedrockAPICounter } from "./bedrock.js";
5
+ import { GoogleAPICounter } from "./google.js";
6
+ import { HeuristicCounter } from "./heuristic.js";
7
+ import { createDefaultCounter } from "./dispatcher.js";
8
+ import { __resetGlobalTokenCounter, getGlobalTokenCounter, setGlobalTokenCounter } from "./global.js";
9
+
10
+ export { AnthropicAPICounter, BedrockAPICounter, GoogleAPICounter, HeuristicCounter, JsTiktokenCounter, __resetGlobalTokenCounter, __resetTiktokenCache, createDefaultCounter, getGlobalTokenCounter, serialiseMessageForCount, serializedToString, setGlobalTokenCounter };
@@ -0,0 +1,63 @@
1
+ import { Message, TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/counters/js-tiktoken.d.ts
4
+
5
+ /**
6
+ * Loose shape of a `js-tiktoken` encoding. Matches the public
7
+ * `Tiktoken` instance returned by `getEncoding(...)` in the upstream
8
+ * package (we only use `encode` / `name`).
9
+ *
10
+ * @internal
11
+ */
12
+ interface TiktokenEncoding {
13
+ readonly name?: string;
14
+ encode(text: string): {
15
+ length: number;
16
+ };
17
+ }
18
+ interface TiktokenModule {
19
+ getEncoding(name: string): TiktokenEncoding;
20
+ encodingForModel?: (model: string) => TiktokenEncoding;
21
+ }
22
+ /**
23
+ * Options for {@link JsTiktokenCounter}.
24
+ *
25
+ * @stable
26
+ */
27
+ interface JsTiktokenCounterOptions {
28
+ /** Encoding name (`'cl100k_base'`, `'o200k_base'`, …). Default `'cl100k_base'`. */
29
+ readonly encoding?: string;
30
+ /** Optional model id used by `encodingForModel`. Falls back to the explicit `encoding`. */
31
+ readonly modelId?: string;
32
+ /**
33
+ * Override the dynamically-loaded module. Used by tests to inject a
34
+ * stub without taking the real `js-tiktoken` peer dependency.
35
+ */
36
+ readonly moduleOverride?: TiktokenModule;
37
+ /** Identifier carried on the produced counter. */
38
+ readonly id?: string;
39
+ }
40
+ /**
41
+ * Counter that delegates to the `js-tiktoken` package. Caches the
42
+ * dynamically-loaded module per process; tests use `moduleOverride`
43
+ * to supply a fixture-shaped substitute.
44
+ *
45
+ * @stable
46
+ */
47
+ declare class JsTiktokenCounter implements TokenCounter {
48
+ #private;
49
+ readonly id: string;
50
+ readonly version: string;
51
+ constructor(options?: JsTiktokenCounterOptions);
52
+ count(messages: ReadonlyArray<Message>): Promise<number>;
53
+ countText(text: string): Promise<number>;
54
+ }
55
+ /**
56
+ * Test-only hook that resets the cached `js-tiktoken` module loader.
57
+ *
58
+ * @internal
59
+ */
60
+ declare function __resetTiktokenCache(): void;
61
+ //#endregion
62
+ export { JsTiktokenCounter, JsTiktokenCounterOptions, __resetTiktokenCache };
63
+ //# sourceMappingURL=js-tiktoken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js-tiktoken.d.ts","names":[],"sources":["../../src/counters/js-tiktoken.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAoIA;;UAhHU,gBAAA;;;;;;UAKA,cAAA;6BACmB;wCACW;;;;;;;UAQvB,wBAAA;;;;;;;;;4BASW;;;;;;;;;;;cAcf,iBAAA,YAA6B;;;;wBAQnB;kBAQC,cAAc,WAAW;2BAUhB;;;;;;;iBAgDjB,oBAAA,CAAA"}
@@ -0,0 +1,73 @@
1
+ import { serialiseMessageForCount, serializedToString } from "./serialize.js";
2
+
3
+ //#region src/counters/js-tiktoken.ts
4
+ let cachedModule = null;
5
+ /**
6
+ * Counter that delegates to the `js-tiktoken` package. Caches the
7
+ * dynamically-loaded module per process; tests use `moduleOverride`
8
+ * to supply a fixture-shaped substitute.
9
+ *
10
+ * @stable
11
+ */
12
+ var JsTiktokenCounter = class {
13
+ id;
14
+ version;
15
+ #encoding;
16
+ #modelId;
17
+ #moduleOverride;
18
+ #encoder = null;
19
+ constructor(options = {}) {
20
+ this.#encoding = options.encoding ?? "cl100k_base";
21
+ if (options.modelId !== void 0) this.#modelId = options.modelId;
22
+ if (options.moduleOverride !== void 0) this.#moduleOverride = options.moduleOverride;
23
+ this.id = options.id ?? `js-tiktoken@${this.#encoding}`;
24
+ this.version = `js-tiktoken-${this.#encoding}-v1`;
25
+ }
26
+ async count(messages) {
27
+ const enc = await this.#getEncoder();
28
+ let total = 0;
29
+ for (const msg of messages) {
30
+ const serialised = serialiseMessageForCount(msg);
31
+ total += enc.encode(serializedToString(serialised)).length;
32
+ }
33
+ return total;
34
+ }
35
+ async countText(text) {
36
+ if (text.length === 0) return 0;
37
+ return (await this.#getEncoder()).encode(text).length;
38
+ }
39
+ async #getEncoder() {
40
+ if (this.#encoder !== null) return this.#encoder;
41
+ const mod = this.#moduleOverride ?? await loadTiktokenModule();
42
+ if (this.#modelId !== void 0 && mod.encodingForModel !== void 0) try {
43
+ this.#encoder = mod.encodingForModel(this.#modelId);
44
+ return this.#encoder;
45
+ } catch {}
46
+ this.#encoder = mod.getEncoding(this.#encoding);
47
+ return this.#encoder;
48
+ }
49
+ };
50
+ async function loadTiktokenModule() {
51
+ if (cachedModule !== null) return cachedModule;
52
+ let mod;
53
+ try {
54
+ mod = await import("js-tiktoken");
55
+ } catch (cause) {
56
+ throw new Error("[graphorin/provider] JsTiktokenCounter requires the 'js-tiktoken' peer dependency. Install it with `pnpm add js-tiktoken` or pass moduleOverride for tests.", { cause });
57
+ }
58
+ if (typeof mod.getEncoding !== "function") throw new Error("[graphorin/provider] JsTiktokenCounter: installed js-tiktoken does not expose getEncoding().");
59
+ cachedModule = mod;
60
+ return cachedModule;
61
+ }
62
+ /**
63
+ * Test-only hook that resets the cached `js-tiktoken` module loader.
64
+ *
65
+ * @internal
66
+ */
67
+ function __resetTiktokenCache() {
68
+ cachedModule = null;
69
+ }
70
+
71
+ //#endregion
72
+ export { JsTiktokenCounter, __resetTiktokenCache };
73
+ //# sourceMappingURL=js-tiktoken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js-tiktoken.js","names":["cachedModule: TiktokenModule | null","#encoding","#modelId","#moduleOverride","#getEncoder","#encoder","mod: TiktokenModule"],"sources":["../../src/counters/js-tiktoken.ts"],"sourcesContent":["/**\n * `JsTiktokenCounter` — default counter for OpenAI / OpenAI-compatible\n * models. The package depends on the `js-tiktoken` peer; when the peer\n * is missing, instantiation throws at first call rather than silently\n * downgrading to a heuristic.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\nimport { serialiseMessageForCount, serializedToString } from './serialize.js';\n\n/**\n * Loose shape of a `js-tiktoken` encoding. Matches the public\n * `Tiktoken` instance returned by `getEncoding(...)` in the upstream\n * package (we only use `encode` / `name`).\n *\n * @internal\n */\ninterface TiktokenEncoding {\n readonly name?: string;\n encode(text: string): { length: number };\n}\n\ninterface TiktokenModule {\n getEncoding(name: string): TiktokenEncoding;\n encodingForModel?: (model: string) => TiktokenEncoding;\n}\n\n/**\n * Options for {@link JsTiktokenCounter}.\n *\n * @stable\n */\nexport interface JsTiktokenCounterOptions {\n /** Encoding name (`'cl100k_base'`, `'o200k_base'`, …). Default `'cl100k_base'`. */\n readonly encoding?: string;\n /** Optional model id used by `encodingForModel`. Falls back to the explicit `encoding`. */\n readonly modelId?: string;\n /**\n * Override the dynamically-loaded module. Used by tests to inject a\n * stub without taking the real `js-tiktoken` peer dependency.\n */\n readonly moduleOverride?: TiktokenModule;\n /** Identifier carried on the produced counter. */\n readonly id?: string;\n}\n\nlet cachedModule: TiktokenModule | null = null;\n\n/**\n * Counter that delegates to the `js-tiktoken` package. Caches the\n * dynamically-loaded module per process; tests use `moduleOverride`\n * to supply a fixture-shaped substitute.\n *\n * @stable\n */\nexport class JsTiktokenCounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #encoding: string;\n readonly #modelId?: string;\n readonly #moduleOverride?: TiktokenModule;\n #encoder: TiktokenEncoding | null = null;\n\n constructor(options: JsTiktokenCounterOptions = {}) {\n this.#encoding = options.encoding ?? 'cl100k_base';\n if (options.modelId !== undefined) this.#modelId = options.modelId;\n if (options.moduleOverride !== undefined) this.#moduleOverride = options.moduleOverride;\n this.id = options.id ?? `js-tiktoken@${this.#encoding}`;\n this.version = `js-tiktoken-${this.#encoding}-v1`;\n }\n\n async count(messages: ReadonlyArray<Message>): Promise<number> {\n const enc = await this.#getEncoder();\n let total = 0;\n for (const msg of messages) {\n const serialised = serialiseMessageForCount(msg);\n total += enc.encode(serializedToString(serialised)).length;\n }\n return total;\n }\n\n async countText(text: string): Promise<number> {\n if (text.length === 0) return 0;\n const enc = await this.#getEncoder();\n return enc.encode(text).length;\n }\n\n async #getEncoder(): Promise<TiktokenEncoding> {\n if (this.#encoder !== null) return this.#encoder;\n const mod = this.#moduleOverride ?? (await loadTiktokenModule());\n if (this.#modelId !== undefined && mod.encodingForModel !== undefined) {\n try {\n this.#encoder = mod.encodingForModel(this.#modelId);\n return this.#encoder;\n } catch {\n // Fall through to the explicit encoding.\n }\n }\n this.#encoder = mod.getEncoding(this.#encoding);\n return this.#encoder;\n }\n}\n\nasync function loadTiktokenModule(): Promise<TiktokenModule> {\n if (cachedModule !== null) return cachedModule;\n let mod: TiktokenModule;\n try {\n mod = (await import('js-tiktoken')) as TiktokenModule;\n } catch (cause) {\n throw new Error(\n \"[graphorin/provider] JsTiktokenCounter requires the 'js-tiktoken' peer dependency. \" +\n 'Install it with `pnpm add js-tiktoken` or pass moduleOverride for tests.',\n { cause },\n );\n }\n if (typeof mod.getEncoding !== 'function') {\n throw new Error(\n '[graphorin/provider] JsTiktokenCounter: installed js-tiktoken does not expose getEncoding().',\n );\n }\n cachedModule = mod;\n return cachedModule;\n}\n\n/**\n * Test-only hook that resets the cached `js-tiktoken` module loader.\n *\n * @internal\n */\nexport function __resetTiktokenCache(): void {\n cachedModule = null;\n}\n"],"mappings":";;;AAiDA,IAAIA,eAAsC;;;;;;;;AAS1C,IAAa,oBAAb,MAAuD;CACrD,AAAS;CACT,AAAS;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,WAAoC;CAEpC,YAAY,UAAoC,EAAE,EAAE;AAClD,QAAKF,WAAY,QAAQ,YAAY;AACrC,MAAI,QAAQ,YAAY,OAAW,OAAKC,UAAW,QAAQ;AAC3D,MAAI,QAAQ,mBAAmB,OAAW,OAAKC,iBAAkB,QAAQ;AACzE,OAAK,KAAK,QAAQ,MAAM,eAAe,MAAKF;AAC5C,OAAK,UAAU,eAAe,MAAKA,SAAU;;CAG/C,MAAM,MAAM,UAAmD;EAC7D,MAAM,MAAM,MAAM,MAAKG,YAAa;EACpC,IAAI,QAAQ;AACZ,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,aAAa,yBAAyB,IAAI;AAChD,YAAS,IAAI,OAAO,mBAAmB,WAAW,CAAC,CAAC;;AAEtD,SAAO;;CAGT,MAAM,UAAU,MAA+B;AAC7C,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,UADY,MAAM,MAAKA,YAAa,EACzB,OAAO,KAAK,CAAC;;CAG1B,OAAMA,aAAyC;AAC7C,MAAI,MAAKC,YAAa,KAAM,QAAO,MAAKA;EACxC,MAAM,MAAM,MAAKF,kBAAoB,MAAM,oBAAoB;AAC/D,MAAI,MAAKD,YAAa,UAAa,IAAI,qBAAqB,OAC1D,KAAI;AACF,SAAKG,UAAW,IAAI,iBAAiB,MAAKH,QAAS;AACnD,UAAO,MAAKG;UACN;AAIV,QAAKA,UAAW,IAAI,YAAY,MAAKJ,SAAU;AAC/C,SAAO,MAAKI;;;AAIhB,eAAe,qBAA8C;AAC3D,KAAI,iBAAiB,KAAM,QAAO;CAClC,IAAIC;AACJ,KAAI;AACF,QAAO,MAAM,OAAO;UACb,OAAO;AACd,QAAM,IAAI,MACR,+JAEA,EAAE,OAAO,CACV;;AAEH,KAAI,OAAO,IAAI,gBAAgB,WAC7B,OAAM,IAAI,MACR,+FACD;AAEH,gBAAe;AACf,QAAO;;;;;;;AAQT,SAAgB,uBAA6B;AAC3C,gBAAe"}