satgate 1.5.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/dist/bin/satgate.d.ts +3 -0
  4. package/dist/bin/satgate.d.ts.map +1 -0
  5. package/dist/bin/satgate.js +7 -0
  6. package/dist/bin/satgate.js.map +1 -0
  7. package/dist/src/auth/allowlist.d.ts +24 -0
  8. package/dist/src/auth/allowlist.d.ts.map +1 -0
  9. package/dist/src/auth/allowlist.js +130 -0
  10. package/dist/src/auth/allowlist.js.map +1 -0
  11. package/dist/src/auth/middleware.d.ts +15 -0
  12. package/dist/src/auth/middleware.d.ts.map +1 -0
  13. package/dist/src/auth/middleware.js +29 -0
  14. package/dist/src/auth/middleware.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.d.ts.map +1 -0
  17. package/dist/src/cli.js +275 -0
  18. package/dist/src/cli.js.map +1 -0
  19. package/dist/src/config.d.ts +133 -0
  20. package/dist/src/config.d.ts.map +1 -0
  21. package/dist/src/config.js +237 -0
  22. package/dist/src/config.js.map +1 -0
  23. package/dist/src/discovery/llms-txt.d.ts +10 -0
  24. package/dist/src/discovery/llms-txt.d.ts.map +1 -0
  25. package/dist/src/discovery/llms-txt.js +25 -0
  26. package/dist/src/discovery/llms-txt.js.map +1 -0
  27. package/dist/src/discovery/openapi.d.ts +8 -0
  28. package/dist/src/discovery/openapi.d.ts.map +1 -0
  29. package/dist/src/discovery/openapi.js +116 -0
  30. package/dist/src/discovery/openapi.js.map +1 -0
  31. package/dist/src/discovery/well-known.d.ts +22 -0
  32. package/dist/src/discovery/well-known.d.ts.map +1 -0
  33. package/dist/src/discovery/well-known.js +44 -0
  34. package/dist/src/discovery/well-known.js.map +1 -0
  35. package/dist/src/index.d.ts +15 -0
  36. package/dist/src/index.d.ts.map +1 -0
  37. package/dist/src/index.js +15 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/lightning.d.ts +12 -0
  40. package/dist/src/lightning.d.ts.map +1 -0
  41. package/dist/src/lightning.js +38 -0
  42. package/dist/src/lightning.js.map +1 -0
  43. package/dist/src/logger.d.ts +16 -0
  44. package/dist/src/logger.d.ts.map +1 -0
  45. package/dist/src/logger.js +99 -0
  46. package/dist/src/logger.js.map +1 -0
  47. package/dist/src/proxy/capacity.d.ts +15 -0
  48. package/dist/src/proxy/capacity.d.ts.map +1 -0
  49. package/dist/src/proxy/capacity.js +28 -0
  50. package/dist/src/proxy/capacity.js.map +1 -0
  51. package/dist/src/proxy/handler.d.ts +27 -0
  52. package/dist/src/proxy/handler.d.ts.map +1 -0
  53. package/dist/src/proxy/handler.js +165 -0
  54. package/dist/src/proxy/handler.js.map +1 -0
  55. package/dist/src/proxy/pricing.d.ts +17 -0
  56. package/dist/src/proxy/pricing.d.ts.map +1 -0
  57. package/dist/src/proxy/pricing.js +42 -0
  58. package/dist/src/proxy/pricing.js.map +1 -0
  59. package/dist/src/proxy/streaming.d.ts +12 -0
  60. package/dist/src/proxy/streaming.d.ts.map +1 -0
  61. package/dist/src/proxy/streaming.js +68 -0
  62. package/dist/src/proxy/streaming.js.map +1 -0
  63. package/dist/src/proxy/token-counter.d.ts +22 -0
  64. package/dist/src/proxy/token-counter.d.ts.map +1 -0
  65. package/dist/src/proxy/token-counter.js +66 -0
  66. package/dist/src/proxy/token-counter.js.map +1 -0
  67. package/dist/src/server.d.ts +9 -0
  68. package/dist/src/server.d.ts.map +1 -0
  69. package/dist/src/server.js +239 -0
  70. package/dist/src/server.js.map +1 -0
  71. package/dist/src/tunnel.d.ts +26 -0
  72. package/dist/src/tunnel.d.ts.map +1 -0
  73. package/dist/src/tunnel.js +78 -0
  74. package/dist/src/tunnel.js.map +1 -0
  75. package/dist/src/x402/facilitator.d.ts +7 -0
  76. package/dist/src/x402/facilitator.d.ts.map +1 -0
  77. package/dist/src/x402/facilitator.js +33 -0
  78. package/dist/src/x402/facilitator.js.map +1 -0
  79. package/package.json +70 -0
@@ -0,0 +1,165 @@
1
+ import { TokenCounter } from './token-counter.js';
2
+ import { createStreamingProxy } from './streaming.js';
3
+ import { resolveModelPrice, tokenCostToSats } from './pricing.js';
4
+ /** Allowed upstream path prefixes — anything else is rejected. */
5
+ const ALLOWED_PATH_PREFIXES = ['/v1/chat/completions', '/v1/completions', '/v1/embeddings'];
6
+ /**
7
+ * Extracts the model name from an OpenAI-compatible request body.
8
+ */
9
+ function extractModel(body) {
10
+ return typeof body.model === 'string' ? body.model : '';
11
+ }
12
+ /**
13
+ * Creates the AI proxy handler.
14
+ *
15
+ * @returns A function that proxies a single inference request to the upstream.
16
+ */
17
+ export function createProxyHandler(deps) {
18
+ return async function handleProxy(req, paymentHash) {
19
+ // Capacity check (before any payment deduction)
20
+ if (!deps.capacity.tryAcquire()) {
21
+ return new Response(JSON.stringify({ error: 'Service at capacity, try again later' }), { status: 503, headers: { 'Content-Type': 'application/json', 'Retry-After': '5' } });
22
+ }
23
+ const start = Date.now();
24
+ let streamingResponse = false;
25
+ try {
26
+ // Validate request path — only allow known OpenAI-compatible endpoints
27
+ const requestPath = new URL(req.url).pathname;
28
+ if (!ALLOWED_PATH_PREFIXES.some(p => requestPath === p)) {
29
+ return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: { 'Content-Type': 'application/json' } });
30
+ }
31
+ // Validate Content-Type
32
+ const contentType = req.headers.get('content-type');
33
+ if (!contentType || !contentType.includes('application/json')) {
34
+ return new Response(JSON.stringify({ error: 'Content-Type must be application/json' }), { status: 415, headers: { 'Content-Type': 'application/json' } });
35
+ }
36
+ // Enforce body size limit
37
+ const contentLength = req.headers.get('content-length');
38
+ if (contentLength !== null) {
39
+ const len = parseInt(contentLength, 10);
40
+ if (!Number.isFinite(len) || len > deps.maxBodySize) {
41
+ return new Response(JSON.stringify({ error: 'Request body too large' }), { status: 413, headers: { 'Content-Type': 'application/json' } });
42
+ }
43
+ }
44
+ // Parse the request body to extract model name
45
+ const bodyText = await req.text();
46
+ if (new TextEncoder().encode(bodyText).byteLength > deps.maxBodySize) {
47
+ return new Response(JSON.stringify({ error: 'Request body too large' }), { status: 413, headers: { 'Content-Type': 'application/json' } });
48
+ }
49
+ let body;
50
+ try {
51
+ const parsed = JSON.parse(bodyText);
52
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
53
+ return new Response(JSON.stringify({ error: 'Request body must be a JSON object' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
54
+ }
55
+ body = parsed;
56
+ }
57
+ catch {
58
+ return new Response(JSON.stringify({ error: 'Invalid JSON body' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
59
+ }
60
+ const model = extractModel(body);
61
+ const pricePerThousand = resolveModelPrice(deps.pricing, model);
62
+ const isStreaming = body.stream === true;
63
+ // Inject stream_options for usage reporting if streaming
64
+ if (isStreaming && !body.stream_options) {
65
+ body.stream_options = { include_usage: true };
66
+ }
67
+ // Build upstream URL
68
+ const url = new URL(req.url);
69
+ const upstreamUrl = `${deps.upstream}${url.pathname}`;
70
+ // Fetch from upstream
71
+ const timeout = deps.upstreamTimeout ?? 120_000;
72
+ let upstreamRes;
73
+ try {
74
+ upstreamRes = await fetch(upstreamUrl, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify(body),
78
+ signal: AbortSignal.timeout(timeout),
79
+ });
80
+ }
81
+ catch (err) {
82
+ // Upstream unreachable - refund estimated cost
83
+ if (paymentHash) {
84
+ deps.reconcile(paymentHash, 0);
85
+ }
86
+ deps.logger?.error('upstream error', {
87
+ endpoint: new URL(req.url).pathname,
88
+ method: req.method,
89
+ latencyMs: Date.now() - start,
90
+ reason: err instanceof Error ? err.message : String(err),
91
+ });
92
+ return new Response(JSON.stringify({ error: 'Upstream inference API unreachable' }), { status: 502, headers: { 'Content-Type': 'application/json' } });
93
+ }
94
+ // If upstream returned an error, refund and return a generic error
95
+ // (don't forward raw upstream body — may leak internal details)
96
+ if (!upstreamRes.ok) {
97
+ if (paymentHash) {
98
+ deps.reconcile(paymentHash, 0);
99
+ }
100
+ // Consume and discard the upstream error body to prevent connection leaks
101
+ await upstreamRes.body?.cancel().catch(() => { });
102
+ const status = upstreamRes.status >= 400 && upstreamRes.status < 600
103
+ ? upstreamRes.status
104
+ : 502;
105
+ return new Response(JSON.stringify({ error: `Upstream returned ${upstreamRes.status}` }), { status, headers: { 'Content-Type': 'application/json' } });
106
+ }
107
+ // Handle streaming response
108
+ if (isStreaming && upstreamRes.body) {
109
+ const { readable } = createStreamingProxy(upstreamRes.body, (tokenCount) => {
110
+ // Release capacity slot when stream ends (not in finally)
111
+ deps.capacity.release();
112
+ if (!deps.flatPricing && paymentHash) {
113
+ const satCost = tokenCostToSats(tokenCount, pricePerThousand);
114
+ deps.reconcile(paymentHash, satCost);
115
+ }
116
+ });
117
+ // Mark as streaming so finally doesn't double-release
118
+ streamingResponse = true;
119
+ return new Response(readable, {
120
+ status: 200,
121
+ headers: {
122
+ 'Content-Type': 'text/event-stream',
123
+ 'Cache-Control': 'no-cache',
124
+ 'Connection': 'keep-alive',
125
+ },
126
+ });
127
+ }
128
+ // Handle non-streaming response — enforce size limit before parsing
129
+ const responseText = await upstreamRes.text();
130
+ if (new TextEncoder().encode(responseText).byteLength > deps.maxBodySize) {
131
+ if (paymentHash)
132
+ deps.reconcile(paymentHash, 0);
133
+ return new Response(JSON.stringify({ error: 'Upstream response too large' }), { status: 502, headers: { 'Content-Type': 'application/json' } });
134
+ }
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ let responseBody;
137
+ try {
138
+ responseBody = JSON.parse(responseText);
139
+ }
140
+ catch {
141
+ return new Response(JSON.stringify({ error: 'Upstream returned invalid JSON' }), { status: 502, headers: { 'Content-Type': 'application/json' } });
142
+ }
143
+ const counter = new TokenCounter();
144
+ if (responseBody && typeof responseBody === 'object' && responseBody.usage) {
145
+ counter.setBufferedUsage(responseBody.usage);
146
+ }
147
+ const tokenCount = counter.finalCount();
148
+ const satCost = tokenCostToSats(tokenCount, pricePerThousand);
149
+ if (!deps.flatPricing && paymentHash) {
150
+ deps.reconcile(paymentHash, satCost);
151
+ }
152
+ return new Response(JSON.stringify(responseBody), {
153
+ status: 200,
154
+ headers: { 'Content-Type': 'application/json' },
155
+ });
156
+ }
157
+ finally {
158
+ // Streaming responses release capacity in the onComplete callback
159
+ if (!streamingResponse) {
160
+ deps.capacity.release();
161
+ }
162
+ }
163
+ };
164
+ }
165
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/proxy/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ModelPricing } from '../config.js';
2
+ /**
3
+ * Resolves the price per 1k tokens for a model.
4
+ *
5
+ * Resolution order:
6
+ * 1. Exact match on model name
7
+ * 2. Case-insensitive match
8
+ * 3. Strip Ollama tag (model:tag -> model) and retry
9
+ * 4. Fall back to default price
10
+ */
11
+ export declare function resolveModelPrice(pricing: ModelPricing, model: string): number;
12
+ /**
13
+ * Converts a token count to a sat cost.
14
+ * Always rounds up (ceil) so the operator is never short-changed.
15
+ */
16
+ export declare function tokenCostToSats(totalTokens: number, pricePerThousand: number): number;
17
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../../src/proxy/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAsB9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAGrF"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Resolves the price per 1k tokens for a model.
3
+ *
4
+ * Resolution order:
5
+ * 1. Exact match on model name
6
+ * 2. Case-insensitive match
7
+ * 3. Strip Ollama tag (model:tag -> model) and retry
8
+ * 4. Fall back to default price
9
+ */
10
+ export function resolveModelPrice(pricing, model) {
11
+ if (!model)
12
+ return pricing.default;
13
+ // Exact match
14
+ if (model in pricing.models)
15
+ return pricing.models[model];
16
+ // Case-insensitive match
17
+ const lower = model.toLowerCase();
18
+ for (const [key, value] of Object.entries(pricing.models)) {
19
+ if (key.toLowerCase() === lower)
20
+ return value;
21
+ }
22
+ // Strip Ollama tag (e.g. llama3:latest -> llama3)
23
+ const colonIdx = lower.indexOf(':');
24
+ if (colonIdx !== -1) {
25
+ const base = lower.slice(0, colonIdx);
26
+ for (const [key, value] of Object.entries(pricing.models)) {
27
+ if (key.toLowerCase() === base)
28
+ return value;
29
+ }
30
+ }
31
+ return pricing.default;
32
+ }
33
+ /**
34
+ * Converts a token count to a sat cost.
35
+ * Always rounds up (ceil) so the operator is never short-changed.
36
+ */
37
+ export function tokenCostToSats(totalTokens, pricePerThousand) {
38
+ if (totalTokens <= 0)
39
+ return 0;
40
+ return Math.ceil(totalTokens * pricePerThousand / 1000);
41
+ }
42
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../../src/proxy/pricing.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAqB,EAAE,KAAa;IACpE,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,OAAO,CAAA;IAElC,cAAc;IACd,IAAI,KAAK,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAEzD,yBAAyB;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,OAAO,KAAK,CAAA;IAC/C,CAAC;IAED,kDAAkD;IAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACnC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACrC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1D,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAA;QAC9C,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAA;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,gBAAwB;IAC3E,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,gBAAgB,GAAG,IAAI,CAAC,CAAA;AACzD,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Creates a ReadableStream that pipes upstream SSE chunks through while counting tokens.
3
+ *
4
+ * @param upstream - The upstream SSE ReadableStream
5
+ * @param onComplete - Called with the final token count after the stream ends or errors
6
+ * @param inactivityTimeoutMs - Max time between chunks before aborting (default: 120s)
7
+ * @returns An object with the readable side
8
+ */
9
+ export declare function createStreamingProxy(upstream: ReadableStream<Uint8Array>, onComplete: (tokenCount: number) => void, inactivityTimeoutMs?: number): {
10
+ readable: ReadableStream<Uint8Array>;
11
+ };
12
+ //# sourceMappingURL=streaming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../../src/proxy/streaming.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,EACpC,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,EACxC,mBAAmB,GAAE,MAAsC,GAC1D;IAAE,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;CAAE,CAsD1C"}
@@ -0,0 +1,68 @@
1
+ import { TokenCounter } from './token-counter.js';
2
+ /** Default inactivity timeout for streaming responses (2 minutes). */
3
+ const DEFAULT_INACTIVITY_TIMEOUT_MS = 120_000;
4
+ /**
5
+ * Creates a ReadableStream that pipes upstream SSE chunks through while counting tokens.
6
+ *
7
+ * @param upstream - The upstream SSE ReadableStream
8
+ * @param onComplete - Called with the final token count after the stream ends or errors
9
+ * @param inactivityTimeoutMs - Max time between chunks before aborting (default: 120s)
10
+ * @returns An object with the readable side
11
+ */
12
+ export function createStreamingProxy(upstream, onComplete, inactivityTimeoutMs = DEFAULT_INACTIVITY_TIMEOUT_MS) {
13
+ const counter = new TokenCounter();
14
+ const decoder = new TextDecoder();
15
+ let completeCalled = false;
16
+ let inactivityTimer;
17
+ function finish() {
18
+ if (completeCalled)
19
+ return;
20
+ completeCalled = true;
21
+ clearTimeout(inactivityTimer);
22
+ onComplete(counter.finalCount());
23
+ }
24
+ const readable = new ReadableStream({
25
+ async start(controller) {
26
+ const reader = upstream.getReader();
27
+ function resetTimer() {
28
+ clearTimeout(inactivityTimer);
29
+ inactivityTimer = setTimeout(() => {
30
+ reader.cancel('inactivity timeout').catch(() => { });
31
+ controller.close();
32
+ finish();
33
+ }, inactivityTimeoutMs);
34
+ }
35
+ resetTimer();
36
+ try {
37
+ while (true) {
38
+ const { done, value } = await reader.read();
39
+ if (done)
40
+ break;
41
+ resetTimer();
42
+ controller.enqueue(value);
43
+ const text = decoder.decode(value, { stream: true });
44
+ counter.ingestSSEChunk(text);
45
+ }
46
+ controller.close();
47
+ }
48
+ catch {
49
+ // Upstream errored — close gracefully
50
+ try {
51
+ controller.close();
52
+ }
53
+ catch { /* already closed */ }
54
+ }
55
+ finally {
56
+ clearTimeout(inactivityTimer);
57
+ finish();
58
+ }
59
+ },
60
+ cancel() {
61
+ // Client disconnected
62
+ clearTimeout(inactivityTimer);
63
+ finish();
64
+ },
65
+ });
66
+ return { readable };
67
+ }
68
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/proxy/streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,sEAAsE;AACtE,MAAM,6BAA6B,GAAG,OAAO,CAAA;AAE7C;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAoC,EACpC,UAAwC,EACxC,sBAA8B,6BAA6B;IAE3D,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;IAClC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,IAAI,cAAc,GAAG,KAAK,CAAA;IAC1B,IAAI,eAA0D,CAAA;IAE9D,SAAS,MAAM;QACb,IAAI,cAAc;YAAE,OAAM;QAC1B,cAAc,GAAG,IAAI,CAAA;QACrB,YAAY,CAAC,eAAe,CAAC,CAAA;QAC7B,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IAClC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAa;QAC9C,KAAK,CAAC,KAAK,CAAC,UAAU;YACpB,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAA;YAEnC,SAAS,UAAU;gBACjB,YAAY,CAAC,eAAe,CAAC,CAAA;gBAC7B,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;oBAChC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;oBACnD,UAAU,CAAC,KAAK,EAAE,CAAA;oBAClB,MAAM,EAAE,CAAA;gBACV,CAAC,EAAE,mBAAmB,CAAC,CAAA;YACzB,CAAC;YAED,UAAU,EAAE,CAAA;YAEZ,IAAI,CAAC;gBACH,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;oBAC3C,IAAI,IAAI;wBAAE,MAAK;oBACf,UAAU,EAAE,CAAA;oBACZ,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;oBACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;oBACpD,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;gBAC9B,CAAC;gBACD,UAAU,CAAC,KAAK,EAAE,CAAA;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,sCAAsC;gBACtC,IAAI,CAAC;oBAAC,UAAU,CAAC,KAAK,EAAE,CAAA;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;YAC3D,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,eAAe,CAAC,CAAA;gBAC7B,MAAM,EAAE,CAAA;YACV,CAAC;QACH,CAAC;QACD,MAAM;YACJ,sBAAsB;YACtB,YAAY,CAAC,eAAe,CAAC,CAAA;YAC7B,MAAM,EAAE,CAAA;QACV,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAA;AACrB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Counts tokens from OpenAI-compatible responses.
3
+ *
4
+ * Priority:
5
+ * 1. Buffered usage (from non-streaming JSON response)
6
+ * 2. Usage from final SSE chunk (stream_options: { include_usage: true })
7
+ * 3. Content chunk count (fallback - 1 chunk ~= 1 token)
8
+ */
9
+ export declare class TokenCounter {
10
+ private bufferedUsage;
11
+ private sseUsage;
12
+ private contentChunkCount;
13
+ /** Set usage from a buffered (non-streaming) JSON response. */
14
+ setBufferedUsage(usage: Record<string, unknown>): void;
15
+ /** Ingest an SSE chunk (may contain multiple events). */
16
+ ingestSSEChunk(chunk: string): void;
17
+ /** Returns the final token count using the best available source.
18
+ * Uses prompt_tokens from usage stats + content chunk count for completion.
19
+ * This avoids billing for reasoning/thinking tokens that some models produce. */
20
+ finalCount(): number;
21
+ }
22
+ //# sourceMappingURL=token-counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-counter.d.ts","sourceRoot":"","sources":["../../../src/proxy/token-counter.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,iBAAiB,CAAI;IAE7B,+DAA+D;IAC/D,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQtD,yDAAyD;IACzD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAkCnC;;sFAEkF;IAClF,UAAU,IAAI,MAAM;CAQrB"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Counts tokens from OpenAI-compatible responses.
3
+ *
4
+ * Priority:
5
+ * 1. Buffered usage (from non-streaming JSON response)
6
+ * 2. Usage from final SSE chunk (stream_options: { include_usage: true })
7
+ * 3. Content chunk count (fallback - 1 chunk ~= 1 token)
8
+ */
9
+ export class TokenCounter {
10
+ bufferedUsage = null;
11
+ sseUsage = null;
12
+ contentChunkCount = 0;
13
+ /** Set usage from a buffered (non-streaming) JSON response. */
14
+ setBufferedUsage(usage) {
15
+ this.bufferedUsage = {
16
+ prompt_tokens: typeof usage.prompt_tokens === 'number' ? usage.prompt_tokens : undefined,
17
+ completion_tokens: typeof usage.completion_tokens === 'number' ? usage.completion_tokens : undefined,
18
+ total_tokens: typeof usage.total_tokens === 'number' ? usage.total_tokens : undefined,
19
+ };
20
+ }
21
+ /** Ingest an SSE chunk (may contain multiple events). */
22
+ ingestSSEChunk(chunk) {
23
+ const lines = chunk.split('\n');
24
+ for (const line of lines) {
25
+ if (!line.startsWith('data: '))
26
+ continue;
27
+ const data = line.slice(6).trim();
28
+ if (data === '[DONE]')
29
+ continue;
30
+ try {
31
+ const parsed = JSON.parse(data);
32
+ // Check for usage in this chunk
33
+ if (parsed.usage) {
34
+ this.sseUsage = {
35
+ prompt_tokens: parsed.usage.prompt_tokens,
36
+ completion_tokens: parsed.usage.completion_tokens,
37
+ total_tokens: parsed.usage.total_tokens,
38
+ };
39
+ }
40
+ // Count content chunks
41
+ const choices = parsed.choices;
42
+ if (Array.isArray(choices)) {
43
+ for (const choice of choices) {
44
+ if (choice.delta?.content !== undefined && choice.delta.content !== '') {
45
+ this.contentChunkCount++;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ catch {
51
+ // Malformed JSON - skip
52
+ }
53
+ }
54
+ }
55
+ /** Returns the final token count using the best available source.
56
+ * Uses prompt_tokens from usage stats + content chunk count for completion.
57
+ * This avoids billing for reasoning/thinking tokens that some models produce. */
58
+ finalCount() {
59
+ const usage = this.bufferedUsage ?? this.sseUsage;
60
+ const promptTokens = usage?.prompt_tokens ?? 0;
61
+ // Content chunk count is the most reliable measure of actual output
62
+ // since it excludes reasoning tokens that models like qwen3 produce
63
+ return promptTokens + this.contentChunkCount;
64
+ }
65
+ }
66
+ //# sourceMappingURL=token-counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-counter.js","sourceRoot":"","sources":["../../../src/proxy/token-counter.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;IACf,aAAa,GAAqB,IAAI,CAAA;IACtC,QAAQ,GAAqB,IAAI,CAAA;IACjC,iBAAiB,GAAG,CAAC,CAAA;IAE7B,+DAA+D;IAC/D,gBAAgB,CAAC,KAA8B;QAC7C,IAAI,CAAC,aAAa,GAAG;YACnB,aAAa,EAAE,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;YACxF,iBAAiB,EAAE,OAAO,KAAK,CAAC,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;YACpG,YAAY,EAAE,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;SACtF,CAAA;IACH,CAAC;IAED,yDAAyD;IACzD,cAAc,CAAC,KAAa;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,SAAQ;YACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACjC,IAAI,IAAI,KAAK,QAAQ;gBAAE,SAAQ;YAE/B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAE/B,gCAAgC;gBAChC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBACjB,IAAI,CAAC,QAAQ,GAAG;wBACd,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;wBACzC,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;wBACjD,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY;qBACxC,CAAA;gBACH,CAAC;gBAED,uBAAuB;gBACvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;gBAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;4BACvE,IAAI,CAAC,iBAAiB,EAAE,CAAA;wBAC1B,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;sFAEkF;IAClF,UAAU;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,QAAQ,CAAA;QACjD,MAAM,YAAY,GAAG,KAAK,EAAE,aAAa,IAAI,CAAC,CAAA;QAE9C,oEAAoE;QACpE,oEAAoE;QACpE,OAAO,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAA;IAC9C,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import { Hono } from 'hono';
2
+ import type { TollBoothEnv } from '@thecryptodonkey/toll-booth/hono';
3
+ import type { TokenTollConfig } from './config.js';
4
+ export interface TokenTollServer {
5
+ app: Hono<TollBoothEnv>;
6
+ close: () => void;
7
+ }
8
+ export declare function createTokenTollServer(config: TokenTollConfig): TokenTollServer;
9
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAU3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAUlD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACvB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAgBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAqO9E"}