@stigg/agent-sdk-js 0.1.0 → 0.2.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/README.md +118 -3
- package/dist/bindings/client/core/types.d.ts +9 -0
- package/dist/bindings/client/core/wrap-client.js +5 -2
- package/dist/bindings/client/gateways/cloudflare/cloudflare-recipe.d.ts +10 -0
- package/dist/bindings/client/gateways/cloudflare/cloudflare-recipe.js +254 -0
- package/dist/bindings/client/gateways/cloudflare/types.d.ts +35 -0
- package/dist/bindings/client/gateways/cloudflare/types.js +2 -0
- package/dist/bindings/client/gateways/databricks/databricks-recipe.d.ts +32 -0
- package/dist/bindings/client/gateways/databricks/databricks-recipe.js +324 -0
- package/dist/bindings/client/gateways/databricks/types.d.ts +30 -0
- package/dist/bindings/client/gateways/databricks/types.js +2 -0
- package/dist/bindings/client/gateways/litellm/litellm-recipe.js +6 -19
- package/dist/bindings/client/gateways/openrouter/openrouter-recipe.js +3 -14
- package/dist/bindings/client/providers/groq/groq-recipe.js +3 -20
- package/dist/bindings/shared/base-url.d.ts +44 -0
- package/dist/bindings/shared/base-url.js +90 -0
- package/dist/bindings/shared/token-usage.d.ts +106 -1
- package/dist/bindings/shared/token-usage.js +162 -1
- package/dist/bindings/shared/types.d.ts +41 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +19 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +5 -1
- package/dist/stigg-agent-sdk.d.ts +1 -1
- package/dist/stigg-agent-sdk.js +13 -0
- package/dist/types.d.ts +35 -1
- package/package.json +7 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* `fromFlatTokenUsage` is read by every vendor that counts that way, not by one of them.
|
|
8
8
|
*/
|
|
9
9
|
import { type Measurement, type Metrics, type MetricsMapper, type TokenUsage } from '../../types';
|
|
10
|
-
import type { BedrockLikeUsage, FlatTokenUsage, GeminiLikeUsage, LangChainLikeUsage, MistralLikeUsage, OllamaLikeUsage, OpenAILikeUsage, ResponsesLikeUsage } from './types';
|
|
10
|
+
import type { BedrockLikeUsage, FlatTokenUsage, GeminiLikeUsage, LangChainLikeUsage, MistralLikeUsage, DatabricksLikeUsage, DatabricksQueryLikeUsage, OllamaLikeUsage, OpenAILikeUsage, ResponsesLikeUsage } from './types';
|
|
11
11
|
export declare function fromFlatTokenUsage(usage: FlatTokenUsage | null | undefined): TokenUsage;
|
|
12
12
|
export declare function fromOpenAIUsage(usage: OpenAILikeUsage | null | undefined): TokenUsage;
|
|
13
13
|
/**
|
|
@@ -140,6 +140,21 @@ export declare const OLLAMA_METRICS: readonly ["prompt_eval_count", "eval_count"
|
|
|
140
140
|
export declare const OPENAI_METRICS: readonly ["prompt_tokens", "completion_tokens", "input_tokens", "output_tokens", "cached_tokens", "total_tokens"];
|
|
141
141
|
/** A gateway speaks the OpenAI shape and adds the price it charged, under its own field name. */
|
|
142
142
|
export declare const GATEWAY_METRICS: readonly ["prompt_tokens", "completion_tokens", "input_tokens", "output_tokens", "cached_tokens", "total_tokens", "cost"];
|
|
143
|
+
/**
|
|
144
|
+
* A gateway that forwards the upstream body untouched, and so reports in two vocabularies.
|
|
145
|
+
*
|
|
146
|
+
* Unlike LiteLLM, which normalises every upstream to the OpenAI wire, Cloudflare AI Gateway's
|
|
147
|
+
* provider-native routes pass the provider's own body straight through - so `/openai` returns
|
|
148
|
+
* OpenAI's counter names and `/anthropic` returns Anthropic's. One recipe serves both wires, so
|
|
149
|
+
* this is the union of what its surfaces can report, which is what `metrics` is defined to be.
|
|
150
|
+
*
|
|
151
|
+
* No `cost`, and that is the defining fact rather than an oversight. Cloudflare states no price on
|
|
152
|
+
* any response: its figure lives in the gateway's own log, is explicitly an estimate, and is zero
|
|
153
|
+
* on a cache hit. So the recipe carries the log id as a dimension and lets spend be reconciled out
|
|
154
|
+
* of band, exactly as the gateway reference prescribes for a price that is only available
|
|
155
|
+
* asynchronously.
|
|
156
|
+
*/
|
|
157
|
+
export declare const CLOUDFLARE_METRICS: readonly ["prompt_tokens", "completion_tokens", "input_tokens", "output_tokens", "cached_tokens", "total_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
143
158
|
/**
|
|
144
159
|
* Bedrock's five, under its own names and with no price among them.
|
|
145
160
|
*
|
|
@@ -154,6 +169,93 @@ export declare const GATEWAY_METRICS: readonly ["prompt_tokens", "completion_tok
|
|
|
154
169
|
export declare const BEDROCK_PATHS: readonly ["inputTokens", "outputTokens", "cacheReadInputTokens", "cacheWriteInputTokens", "totalTokens"];
|
|
155
170
|
/** Those five. Flat, so the paths are the names. */
|
|
156
171
|
export declare const BEDROCK_METRICS: readonly ["inputTokens", "outputTokens", "cacheReadInputTokens", "cacheWriteInputTokens", "totalTokens"];
|
|
172
|
+
/**
|
|
173
|
+
* Databricks' six, under its own names - and its own names are two vendors' names at once.
|
|
174
|
+
*
|
|
175
|
+
* The first three are OpenAI's spelling, because the serving endpoints speak the OpenAI wire. The
|
|
176
|
+
* two cache counters are *Anthropic's* spelling, flat on the usage block rather than nested under
|
|
177
|
+
* `prompt_tokens_details` where an OpenAI-shaped reader would look for them, and `reasoning_tokens`
|
|
178
|
+
* is Databricks' own. Reported exactly as Databricks spells them, per the rule that a number has to
|
|
179
|
+
* reconcile against the vendor's own dashboard - renaming the cache pair to `cached_tokens` would
|
|
180
|
+
* make one account read as two vocabularies and find nothing where it looked.
|
|
181
|
+
*
|
|
182
|
+
* No `cost`, and that is the fact that most shapes this binding: Databricks states no price on any
|
|
183
|
+
* response, on either route. See `databricks-recipe.ts` for what that costs a run.
|
|
184
|
+
*/
|
|
185
|
+
export declare const DATABRICKS_PATHS: readonly ["prompt_tokens", "completion_tokens", "total_tokens", "reasoning_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
186
|
+
/**
|
|
187
|
+
* The same counters as the Responses API spells them.
|
|
188
|
+
*
|
|
189
|
+
* Databricks serves both OpenAI surfaces, and they do not share field names: Chat Completions
|
|
190
|
+
* sends `prompt_tokens`/`completion_tokens` and Responses sends `input_tokens`/`output_tokens`.
|
|
191
|
+
* Reading a Responses body with the list above finds neither, so the call would report its total
|
|
192
|
+
* and nothing else - a number with no breakdown under it, which prices wrongly rather than
|
|
193
|
+
* visibly. Each surface reads its own list.
|
|
194
|
+
*/
|
|
195
|
+
export declare const DATABRICKS_RESPONSES_PATHS: readonly ["input_tokens", "output_tokens", "total_tokens", "reasoning_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
196
|
+
/** The union across both surfaces: what a Databricks call can report, which is what `metrics` is. */
|
|
197
|
+
export declare const DATABRICKS_METRICS: readonly ["prompt_tokens", "completion_tokens", "input_tokens", "output_tokens", "total_tokens", "reasoning_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
198
|
+
/**
|
|
199
|
+
* Where the **first-party client's** counters are read and what each is reported as.
|
|
200
|
+
*
|
|
201
|
+
* The second map in this file, and it exists for precisely the reason the Mistral one does.
|
|
202
|
+
* `@databricks/sdk-modelservingquery` is generated, and its zod schema parses `prompt_tokens` off
|
|
203
|
+
* the wire and transforms it to `promptTokens` before the caller ever sees it - so following the
|
|
204
|
+
* key it was read from would report a camelCase feature id for a call that the *same workspace*
|
|
205
|
+
* reports in snake_case when the application holds an `OpenAI` client instead.
|
|
206
|
+
*
|
|
207
|
+
* That is not a cosmetic difference. It is one Databricks account producing two different feature
|
|
208
|
+
* ids depending on which package the application happened to import, splitting one bill across two
|
|
209
|
+
* meters - which is the failure `pickRenamedMetrics` was built to prevent. Three entries rather
|
|
210
|
+
* than six because `ExternalModelUsageElement` carries only three; the cache pair and
|
|
211
|
+
* `reasoning_tokens` have no field on it, and a counter with no field is absent rather than zero.
|
|
212
|
+
*/
|
|
213
|
+
export declare const DATABRICKS_QUERY_METRIC_NAME_MAP: {
|
|
214
|
+
readonly promptTokens: "prompt_tokens";
|
|
215
|
+
readonly completionTokens: "completion_tokens";
|
|
216
|
+
readonly totalTokens: "total_tokens";
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Workers AI's three, and only three.
|
|
220
|
+
*
|
|
221
|
+
* OpenAI's spelling, flat, with no cache detail nested underneath - so this is a narrower list
|
|
222
|
+
* than `OPENAI_CHAT_METRICS` rather than a reuse of it. Declaring `cached_tokens` here would type
|
|
223
|
+
* a pricing table against a counter Workers AI never sends.
|
|
224
|
+
*
|
|
225
|
+
* No neurons either, which is the counter a reader might expect. Cloudflare bills Workers AI in
|
|
226
|
+
* Neurons, and publishes none of them on any response - binding or REST - so the conversion from
|
|
227
|
+
* tokens is a price list this SDK does not hold. Tokens are what the response states and tokens
|
|
228
|
+
* are what this reports.
|
|
229
|
+
*/
|
|
230
|
+
export declare const WORKERS_AI_PATHS: readonly ["prompt_tokens", "completion_tokens", "total_tokens"];
|
|
231
|
+
/** Those three. Flat, so the paths are the names. */
|
|
232
|
+
export declare const WORKERS_AI_METRICS: readonly ["prompt_tokens", "completion_tokens", "total_tokens"];
|
|
233
|
+
/**
|
|
234
|
+
* Databricks' usage block -> the neutral digest.
|
|
235
|
+
*
|
|
236
|
+
* `input` is `prompt_tokens` verbatim, *not* reduced by the cache reads the way `fromOpenAIUsage`
|
|
237
|
+
* reduces its own. OpenAI documents its cached tokens as counted inside `prompt_tokens`, so
|
|
238
|
+
* subtracting there is undoing a documented overlap. Databricks documents no such thing, and the
|
|
239
|
+
* spelling it chose for the pair is Anthropic's - whose cache counters sit *beside* the input
|
|
240
|
+
* count rather than inside it. So the Anthropic convention is the better guess and the overlap is
|
|
241
|
+
* left alone; the stated total is read rather than computed, which keeps the one number that gets
|
|
242
|
+
* invoiced right whichever way the overlap actually runs. The README's overlap table says the
|
|
243
|
+
* direction is unverified for Databricks, because it is.
|
|
244
|
+
*/
|
|
245
|
+
export declare function fromDatabricksUsage(usage: DatabricksLikeUsage | null | undefined): TokenUsage;
|
|
246
|
+
/**
|
|
247
|
+
* The same block as read through the first-party client -> the neutral digest.
|
|
248
|
+
*
|
|
249
|
+
* Its own reader rather than a call into `fromDatabricksUsage`, because the field names genuinely
|
|
250
|
+
* differ: the generated client camelCases the wire on the way in, so a snake_case reader finds
|
|
251
|
+
* none of these and returns an all-zero digest without throwing. That is the shape of failure the
|
|
252
|
+
* invariants call out - an extractor that meters every call at nothing and trips no test.
|
|
253
|
+
*
|
|
254
|
+
* No cache arithmetic to get wrong here, which is the one simplification over the wire-shaped
|
|
255
|
+
* reader: `ExternalModelUsageElement` declares no cache counters at all, so there is no overlap of
|
|
256
|
+
* unknown direction to leave alone. The stated total is still read rather than recomputed.
|
|
257
|
+
*/
|
|
258
|
+
export declare function fromDatabricksQueryUsage(usage: DatabricksQueryLikeUsage | null | undefined): TokenUsage;
|
|
157
259
|
/** The leaf a dotted path reports under - `prompt_tokens_details.cached_tokens` -> `cached_tokens`. */
|
|
158
260
|
export declare const metricName: (path: string) => string;
|
|
159
261
|
/**
|
|
@@ -188,6 +290,9 @@ export declare const METRIC_PATHS: {
|
|
|
188
290
|
readonly typesafe: readonly ["input_tokens", "output_tokens"];
|
|
189
291
|
readonly gemini: readonly ["promptTokenCount", "candidatesTokenCount", "cachedContentTokenCount", "thoughtsTokenCount", "toolUsePromptTokenCount", "totalTokenCount"];
|
|
190
292
|
readonly bedrock: readonly ["inputTokens", "outputTokens", "cacheReadInputTokens", "cacheWriteInputTokens", "totalTokens"];
|
|
293
|
+
readonly databricks: readonly ["prompt_tokens", "completion_tokens", "total_tokens", "reasoning_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
294
|
+
readonly databricksResponses: readonly ["input_tokens", "output_tokens", "total_tokens", "reasoning_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"];
|
|
295
|
+
readonly workersAi: readonly ["prompt_tokens", "completion_tokens", "total_tokens"];
|
|
191
296
|
readonly ollama: readonly ["prompt_eval_count", "eval_count"];
|
|
192
297
|
};
|
|
193
298
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizedMetrics = exports.addMetrics = exports.METRIC_PATHS = exports.metricNamesMatchPaths = exports.COMPUTED_TOTALS = exports.metricName = exports.BEDROCK_METRICS = exports.BEDROCK_PATHS = exports.GATEWAY_METRICS = exports.OPENAI_METRICS = exports.OLLAMA_METRICS = exports.OLLAMA_COMPONENT_METRICS = exports.TYPESAFE_METRICS = exports.TYPESAFE_COMPONENT_METRICS = exports.MISTRAL_METRICS = exports.MISTRAL_METRIC_NAME_MAP = exports.OPENAI_RESPONSES_METRICS = exports.OPENAI_CHAT_METRICS = exports.GEMINI_COMPONENT_METRICS = exports.GEMINI_METRICS = exports.ANTHROPIC_METRICS = exports.ANTHROPIC_COMPONENT_METRICS = exports.TOTAL_TOKENS = exports.addTokenUsage = void 0;
|
|
3
|
+
exports.normalizedMetrics = exports.addMetrics = exports.METRIC_PATHS = exports.metricNamesMatchPaths = exports.COMPUTED_TOTALS = exports.metricName = exports.WORKERS_AI_METRICS = exports.WORKERS_AI_PATHS = exports.DATABRICKS_QUERY_METRIC_NAME_MAP = exports.DATABRICKS_METRICS = exports.DATABRICKS_RESPONSES_PATHS = exports.DATABRICKS_PATHS = exports.BEDROCK_METRICS = exports.BEDROCK_PATHS = exports.CLOUDFLARE_METRICS = exports.GATEWAY_METRICS = exports.OPENAI_METRICS = exports.OLLAMA_METRICS = exports.OLLAMA_COMPONENT_METRICS = exports.TYPESAFE_METRICS = exports.TYPESAFE_COMPONENT_METRICS = exports.MISTRAL_METRICS = exports.MISTRAL_METRIC_NAME_MAP = exports.OPENAI_RESPONSES_METRICS = exports.OPENAI_CHAT_METRICS = exports.GEMINI_COMPONENT_METRICS = exports.GEMINI_METRICS = exports.ANTHROPIC_METRICS = exports.ANTHROPIC_COMPONENT_METRICS = exports.TOTAL_TOKENS = exports.addTokenUsage = void 0;
|
|
4
4
|
exports.fromFlatTokenUsage = fromFlatTokenUsage;
|
|
5
5
|
exports.fromOpenAIUsage = fromOpenAIUsage;
|
|
6
6
|
exports.fromResponsesUsage = fromResponsesUsage;
|
|
@@ -10,6 +10,8 @@ exports.fromGeminiUsage = fromGeminiUsage;
|
|
|
10
10
|
exports.fromMistralUsage = fromMistralUsage;
|
|
11
11
|
exports.fromOllamaUsage = fromOllamaUsage;
|
|
12
12
|
exports.normalizeUsage = normalizeUsage;
|
|
13
|
+
exports.fromDatabricksUsage = fromDatabricksUsage;
|
|
14
|
+
exports.fromDatabricksQueryUsage = fromDatabricksQueryUsage;
|
|
13
15
|
exports.withComputedTotal = withComputedTotal;
|
|
14
16
|
exports.pickMetrics = pickMetrics;
|
|
15
17
|
exports.pickRenamedMetrics = pickRenamedMetrics;
|
|
@@ -404,6 +406,25 @@ exports.OPENAI_METRICS = [
|
|
|
404
406
|
];
|
|
405
407
|
/** A gateway speaks the OpenAI shape and adds the price it charged, under its own field name. */
|
|
406
408
|
exports.GATEWAY_METRICS = [...exports.OPENAI_METRICS, 'cost'];
|
|
409
|
+
/**
|
|
410
|
+
* A gateway that forwards the upstream body untouched, and so reports in two vocabularies.
|
|
411
|
+
*
|
|
412
|
+
* Unlike LiteLLM, which normalises every upstream to the OpenAI wire, Cloudflare AI Gateway's
|
|
413
|
+
* provider-native routes pass the provider's own body straight through - so `/openai` returns
|
|
414
|
+
* OpenAI's counter names and `/anthropic` returns Anthropic's. One recipe serves both wires, so
|
|
415
|
+
* this is the union of what its surfaces can report, which is what `metrics` is defined to be.
|
|
416
|
+
*
|
|
417
|
+
* No `cost`, and that is the defining fact rather than an oversight. Cloudflare states no price on
|
|
418
|
+
* any response: its figure lives in the gateway's own log, is explicitly an estimate, and is zero
|
|
419
|
+
* on a cache hit. So the recipe carries the log id as a dimension and lets spend be reconciled out
|
|
420
|
+
* of band, exactly as the gateway reference prescribes for a price that is only available
|
|
421
|
+
* asynchronously.
|
|
422
|
+
*/
|
|
423
|
+
exports.CLOUDFLARE_METRICS = [
|
|
424
|
+
...exports.OPENAI_METRICS,
|
|
425
|
+
'cache_read_input_tokens',
|
|
426
|
+
'cache_creation_input_tokens',
|
|
427
|
+
];
|
|
407
428
|
/**
|
|
408
429
|
* Bedrock's five, under its own names and with no price among them.
|
|
409
430
|
*
|
|
@@ -424,6 +445,143 @@ exports.BEDROCK_PATHS = [
|
|
|
424
445
|
];
|
|
425
446
|
/** Those five. Flat, so the paths are the names. */
|
|
426
447
|
exports.BEDROCK_METRICS = exports.BEDROCK_PATHS;
|
|
448
|
+
/**
|
|
449
|
+
* Databricks' six, under its own names - and its own names are two vendors' names at once.
|
|
450
|
+
*
|
|
451
|
+
* The first three are OpenAI's spelling, because the serving endpoints speak the OpenAI wire. The
|
|
452
|
+
* two cache counters are *Anthropic's* spelling, flat on the usage block rather than nested under
|
|
453
|
+
* `prompt_tokens_details` where an OpenAI-shaped reader would look for them, and `reasoning_tokens`
|
|
454
|
+
* is Databricks' own. Reported exactly as Databricks spells them, per the rule that a number has to
|
|
455
|
+
* reconcile against the vendor's own dashboard - renaming the cache pair to `cached_tokens` would
|
|
456
|
+
* make one account read as two vocabularies and find nothing where it looked.
|
|
457
|
+
*
|
|
458
|
+
* No `cost`, and that is the fact that most shapes this binding: Databricks states no price on any
|
|
459
|
+
* response, on either route. See `databricks-recipe.ts` for what that costs a run.
|
|
460
|
+
*/
|
|
461
|
+
exports.DATABRICKS_PATHS = [
|
|
462
|
+
'prompt_tokens',
|
|
463
|
+
'completion_tokens',
|
|
464
|
+
'total_tokens',
|
|
465
|
+
'reasoning_tokens',
|
|
466
|
+
'cache_read_input_tokens',
|
|
467
|
+
'cache_creation_input_tokens',
|
|
468
|
+
];
|
|
469
|
+
/**
|
|
470
|
+
* The same counters as the Responses API spells them.
|
|
471
|
+
*
|
|
472
|
+
* Databricks serves both OpenAI surfaces, and they do not share field names: Chat Completions
|
|
473
|
+
* sends `prompt_tokens`/`completion_tokens` and Responses sends `input_tokens`/`output_tokens`.
|
|
474
|
+
* Reading a Responses body with the list above finds neither, so the call would report its total
|
|
475
|
+
* and nothing else - a number with no breakdown under it, which prices wrongly rather than
|
|
476
|
+
* visibly. Each surface reads its own list.
|
|
477
|
+
*/
|
|
478
|
+
exports.DATABRICKS_RESPONSES_PATHS = [
|
|
479
|
+
'input_tokens',
|
|
480
|
+
'output_tokens',
|
|
481
|
+
'total_tokens',
|
|
482
|
+
'reasoning_tokens',
|
|
483
|
+
'cache_read_input_tokens',
|
|
484
|
+
'cache_creation_input_tokens',
|
|
485
|
+
];
|
|
486
|
+
/** The union across both surfaces: what a Databricks call can report, which is what `metrics` is. */
|
|
487
|
+
exports.DATABRICKS_METRICS = [
|
|
488
|
+
'prompt_tokens',
|
|
489
|
+
'completion_tokens',
|
|
490
|
+
'input_tokens',
|
|
491
|
+
'output_tokens',
|
|
492
|
+
'total_tokens',
|
|
493
|
+
'reasoning_tokens',
|
|
494
|
+
'cache_read_input_tokens',
|
|
495
|
+
'cache_creation_input_tokens',
|
|
496
|
+
];
|
|
497
|
+
/**
|
|
498
|
+
* Where the **first-party client's** counters are read and what each is reported as.
|
|
499
|
+
*
|
|
500
|
+
* The second map in this file, and it exists for precisely the reason the Mistral one does.
|
|
501
|
+
* `@databricks/sdk-modelservingquery` is generated, and its zod schema parses `prompt_tokens` off
|
|
502
|
+
* the wire and transforms it to `promptTokens` before the caller ever sees it - so following the
|
|
503
|
+
* key it was read from would report a camelCase feature id for a call that the *same workspace*
|
|
504
|
+
* reports in snake_case when the application holds an `OpenAI` client instead.
|
|
505
|
+
*
|
|
506
|
+
* That is not a cosmetic difference. It is one Databricks account producing two different feature
|
|
507
|
+
* ids depending on which package the application happened to import, splitting one bill across two
|
|
508
|
+
* meters - which is the failure `pickRenamedMetrics` was built to prevent. Three entries rather
|
|
509
|
+
* than six because `ExternalModelUsageElement` carries only three; the cache pair and
|
|
510
|
+
* `reasoning_tokens` have no field on it, and a counter with no field is absent rather than zero.
|
|
511
|
+
*/
|
|
512
|
+
exports.DATABRICKS_QUERY_METRIC_NAME_MAP = {
|
|
513
|
+
promptTokens: 'prompt_tokens',
|
|
514
|
+
completionTokens: 'completion_tokens',
|
|
515
|
+
totalTokens: 'total_tokens',
|
|
516
|
+
};
|
|
517
|
+
/**
|
|
518
|
+
* Workers AI's three, and only three.
|
|
519
|
+
*
|
|
520
|
+
* OpenAI's spelling, flat, with no cache detail nested underneath - so this is a narrower list
|
|
521
|
+
* than `OPENAI_CHAT_METRICS` rather than a reuse of it. Declaring `cached_tokens` here would type
|
|
522
|
+
* a pricing table against a counter Workers AI never sends.
|
|
523
|
+
*
|
|
524
|
+
* No neurons either, which is the counter a reader might expect. Cloudflare bills Workers AI in
|
|
525
|
+
* Neurons, and publishes none of them on any response - binding or REST - so the conversion from
|
|
526
|
+
* tokens is a price list this SDK does not hold. Tokens are what the response states and tokens
|
|
527
|
+
* are what this reports.
|
|
528
|
+
*/
|
|
529
|
+
exports.WORKERS_AI_PATHS = ['prompt_tokens', 'completion_tokens', 'total_tokens'];
|
|
530
|
+
/** Those three. Flat, so the paths are the names. */
|
|
531
|
+
exports.WORKERS_AI_METRICS = exports.WORKERS_AI_PATHS;
|
|
532
|
+
/**
|
|
533
|
+
* Databricks' usage block -> the neutral digest.
|
|
534
|
+
*
|
|
535
|
+
* `input` is `prompt_tokens` verbatim, *not* reduced by the cache reads the way `fromOpenAIUsage`
|
|
536
|
+
* reduces its own. OpenAI documents its cached tokens as counted inside `prompt_tokens`, so
|
|
537
|
+
* subtracting there is undoing a documented overlap. Databricks documents no such thing, and the
|
|
538
|
+
* spelling it chose for the pair is Anthropic's - whose cache counters sit *beside* the input
|
|
539
|
+
* count rather than inside it. So the Anthropic convention is the better guess and the overlap is
|
|
540
|
+
* left alone; the stated total is read rather than computed, which keeps the one number that gets
|
|
541
|
+
* invoiced right whichever way the overlap actually runs. The README's overlap table says the
|
|
542
|
+
* direction is unverified for Databricks, because it is.
|
|
543
|
+
*/
|
|
544
|
+
function fromDatabricksUsage(usage) {
|
|
545
|
+
if (!usage)
|
|
546
|
+
return (0, types_1.emptyTokenUsage)();
|
|
547
|
+
return {
|
|
548
|
+
// Read, never computed: Databricks states a total, and a stated total beats a reassembled one
|
|
549
|
+
// precisely because the overlap above is unknown.
|
|
550
|
+
...withTotal(n(usage.total_tokens)),
|
|
551
|
+
// Both spellings, because one Databricks workspace serves both OpenAI surfaces: Chat
|
|
552
|
+
// Completions sends `prompt_tokens`/`completion_tokens` and Responses sends
|
|
553
|
+
// `input_tokens`/`output_tokens`. A body carries one pair or the other, never both, so the
|
|
554
|
+
// fallback cannot double-count - and reading only the chat pair left `normalizedMetrics` and
|
|
555
|
+
// every custom `TokenUsage` mapper seeing zeroes for a Responses call.
|
|
556
|
+
input: n(usage.prompt_tokens ?? usage.input_tokens),
|
|
557
|
+
output: n(usage.completion_tokens ?? usage.output_tokens),
|
|
558
|
+
cacheRead: n(usage.cache_read_input_tokens),
|
|
559
|
+
cacheCreate: n(usage.cache_creation_input_tokens),
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* The same block as read through the first-party client -> the neutral digest.
|
|
564
|
+
*
|
|
565
|
+
* Its own reader rather than a call into `fromDatabricksUsage`, because the field names genuinely
|
|
566
|
+
* differ: the generated client camelCases the wire on the way in, so a snake_case reader finds
|
|
567
|
+
* none of these and returns an all-zero digest without throwing. That is the shape of failure the
|
|
568
|
+
* invariants call out - an extractor that meters every call at nothing and trips no test.
|
|
569
|
+
*
|
|
570
|
+
* No cache arithmetic to get wrong here, which is the one simplification over the wire-shaped
|
|
571
|
+
* reader: `ExternalModelUsageElement` declares no cache counters at all, so there is no overlap of
|
|
572
|
+
* unknown direction to leave alone. The stated total is still read rather than recomputed.
|
|
573
|
+
*/
|
|
574
|
+
function fromDatabricksQueryUsage(usage) {
|
|
575
|
+
if (!usage)
|
|
576
|
+
return (0, types_1.emptyTokenUsage)();
|
|
577
|
+
return {
|
|
578
|
+
...withTotal(n(usage.totalTokens)),
|
|
579
|
+
input: n(usage.promptTokens),
|
|
580
|
+
output: n(usage.completionTokens),
|
|
581
|
+
cacheRead: 0,
|
|
582
|
+
cacheCreate: 0,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
427
585
|
/** The leaf a dotted path reports under - `prompt_tokens_details.cached_tokens` -> `cached_tokens`. */
|
|
428
586
|
const metricName = (path) => path.split('.').pop();
|
|
429
587
|
exports.metricName = metricName;
|
|
@@ -470,6 +628,9 @@ exports.METRIC_PATHS = {
|
|
|
470
628
|
typesafe: TYPESAFE_PATHS,
|
|
471
629
|
gemini: GEMINI_PATHS,
|
|
472
630
|
bedrock: exports.BEDROCK_PATHS,
|
|
631
|
+
databricks: exports.DATABRICKS_PATHS,
|
|
632
|
+
databricksResponses: exports.DATABRICKS_RESPONSES_PATHS,
|
|
633
|
+
workersAi: exports.WORKERS_AI_PATHS,
|
|
473
634
|
ollama: exports.OLLAMA_COMPONENT_METRICS,
|
|
474
635
|
};
|
|
475
636
|
/**
|
|
@@ -31,6 +31,47 @@ export type OpenAILikeUsage = {
|
|
|
31
31
|
cache_write_tokens?: number | null;
|
|
32
32
|
} | null;
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Databricks model serving `usage` - OpenAI's three counters, with Anthropic's spelling for cache.
|
|
36
|
+
*
|
|
37
|
+
* The mixed vocabulary is Databricks' own and is reported verbatim rather than normalised to one
|
|
38
|
+
* side or the other. The first three come back from every serving endpoint; the last three are
|
|
39
|
+
* Foundation Model API extensions and are absent on the external-model route, where the body is
|
|
40
|
+
* whatever the upstream provider's counters normalise to.
|
|
41
|
+
*/
|
|
42
|
+
export type DatabricksLikeUsage = {
|
|
43
|
+
prompt_tokens?: number | null;
|
|
44
|
+
completion_tokens?: number | null;
|
|
45
|
+
/** Databricks states its own total, so it is read rather than reassembled from the parts. */
|
|
46
|
+
total_tokens?: number | null;
|
|
47
|
+
/** Reasoning models only. */
|
|
48
|
+
reasoning_tokens?: number | null;
|
|
49
|
+
cache_read_input_tokens?: number | null;
|
|
50
|
+
cache_creation_input_tokens?: number | null;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The same block as above, as `@databricks/sdk-modelservingquery` hands it over.
|
|
54
|
+
*
|
|
55
|
+
* Databricks' own `ExternalModelUsageElement`. The **wire** is the snake_case one above - the
|
|
56
|
+
* generated client's zod schema parses `prompt_tokens` and transforms it to `promptTokens` on the
|
|
57
|
+
* way in, exactly as Mistral's generated client does to `UsageInfo`. So this is a second spelling
|
|
58
|
+
* of one vendor's counters rather than a second set of counters, and it is read through
|
|
59
|
+
* `DATABRICKS_QUERY_METRIC_NAME_MAP` so both spellings report under the wire's names.
|
|
60
|
+
*
|
|
61
|
+
* Three, not six: the cache pair and `reasoning_tokens` have no field on this type at all, so a
|
|
62
|
+
* call made through the first-party client reports the three it can and nothing more. Absent, not
|
|
63
|
+
* zero - and the reason the native surface's metric list is a subset of the recipe's.
|
|
64
|
+
*
|
|
65
|
+
* Written out rather than aliased to {@link MistralLikeUsage}, which it currently matches
|
|
66
|
+
* field-for-field: these are two unrelated vendor contracts that happen to agree today, and
|
|
67
|
+
* collapsing them would make a change on either side look like a change to both.
|
|
68
|
+
*/
|
|
69
|
+
export type DatabricksQueryLikeUsage = {
|
|
70
|
+
promptTokens?: number | null;
|
|
71
|
+
completionTokens?: number | null;
|
|
72
|
+
/** Databricks states its own total here too, so it is read rather than reassembled. */
|
|
73
|
+
totalTokens?: number | null;
|
|
74
|
+
};
|
|
34
75
|
/** OpenAI's Responses API `usage` - the same counts as above under different names. */
|
|
35
76
|
export type ResponsesLikeUsage = {
|
|
36
77
|
input_tokens?: number | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -46,9 +46,15 @@ export { mistralRecipe } from './bindings/client/providers/mistral/mistral-recip
|
|
|
46
46
|
export type { MistralMetric, MistralMetrics } from './bindings/client/providers/mistral/mistral-recipe';
|
|
47
47
|
export { ollamaRecipe } from './bindings/client/providers/ollama/ollama-recipe';
|
|
48
48
|
export type { OllamaMetric, OllamaMetrics } from './bindings/client/providers/ollama/ollama-recipe';
|
|
49
|
+
export { databricksRecipe, databricksExternalRecipe } from './bindings/client/gateways/databricks/databricks-recipe';
|
|
50
|
+
export type { DatabricksMetricName, DatabricksMetrics } from './bindings/client/gateways/databricks/databricks-recipe';
|
|
51
|
+
export type { DatabricksRecipeOptions } from './bindings/client/gateways/databricks/types';
|
|
49
52
|
export { bedrockRecipe } from './bindings/client/gateways/bedrock/bedrock-recipe';
|
|
50
53
|
export type { BedrockMetricName, BedrockMetrics } from './bindings/client/gateways/bedrock/bedrock-recipe';
|
|
51
54
|
export type { BedrockRecipeOptions } from './bindings/client/gateways/bedrock/types';
|
|
55
|
+
export { cloudflareRecipe } from './bindings/client/gateways/cloudflare/cloudflare-recipe';
|
|
56
|
+
export type { CloudflareMetricName, CloudflareMetrics } from './bindings/client/gateways/cloudflare/cloudflare-recipe';
|
|
57
|
+
export type { CloudflareRecipeOptions } from './bindings/client/gateways/cloudflare/types';
|
|
52
58
|
export { typesafeRecipe, estimateFromState } from './bindings/client/providers/typesafe/typesafe-recipe';
|
|
53
59
|
export type { TypeSafeMetric, TypeSafeMetrics } from './bindings/client/providers/typesafe/typesafe-recipe';
|
|
54
60
|
export { weightedMetrics } from './bindings/shared/weighted-metrics';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeUsage = exports.normalizedMetrics = exports.weightedMetrics = exports.estimateFromState = exports.typesafeRecipe = exports.bedrockRecipe = exports.ollamaRecipe = exports.mistralRecipe = exports.groqRecipe = exports.litellmRecipe = exports.openrouterRecipe = exports.estimateFromMaxOutputTokens = exports.geminiChatRecipe = exports.geminiRecipe = exports.openAIRecipe = exports.estimateFromMaxTokens = exports.anthropicRecipe = exports.hasMethod = exports.createClientRegistry = exports.wrapClient = exports.toMeasurement = exports.entitlementRef = exports.entitlementKind = exports.entitlementId = exports.emptyTokenUsage = exports.vendorMetrics = exports.unitsPerTokens = exports.looksLikeGovernanceNotEnabled = exports.isGovernanceNotEnabled = exports.isAccessDenied = exports.isEngineError = exports.GovernanceNotEnabledError = exports.AccessDeniedError = exports.EngineError = exports.noopLogger = exports.consoleLogger = exports.parseGovernanceHeaders = exports.GOVERNANCE_HEADERS = exports.remedyMessage = exports.describeDecision = exports.flattenRaw = exports.RAW_PREFIX = exports.StiggAgentSDK = void 0;
|
|
3
|
+
exports.normalizeUsage = exports.normalizedMetrics = exports.weightedMetrics = exports.estimateFromState = exports.typesafeRecipe = exports.cloudflareRecipe = exports.bedrockRecipe = exports.databricksExternalRecipe = exports.databricksRecipe = exports.ollamaRecipe = exports.mistralRecipe = exports.groqRecipe = exports.litellmRecipe = exports.openrouterRecipe = exports.estimateFromMaxOutputTokens = exports.geminiChatRecipe = exports.geminiRecipe = exports.openAIRecipe = exports.estimateFromMaxTokens = exports.anthropicRecipe = exports.hasMethod = exports.createClientRegistry = exports.wrapClient = exports.toMeasurement = exports.entitlementRef = exports.entitlementKind = exports.entitlementId = exports.emptyTokenUsage = exports.vendorMetrics = exports.unitsPerTokens = exports.looksLikeGovernanceNotEnabled = exports.isGovernanceNotEnabled = exports.isAccessDenied = exports.isEngineError = exports.GovernanceNotEnabledError = exports.AccessDeniedError = exports.EngineError = exports.noopLogger = exports.consoleLogger = exports.parseGovernanceHeaders = exports.GOVERNANCE_HEADERS = exports.remedyMessage = exports.describeDecision = exports.flattenRaw = exports.RAW_PREFIX = exports.StiggAgentSDK = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Public surface.
|
|
6
6
|
*
|
|
@@ -92,10 +92,28 @@ Object.defineProperty(exports, "ollamaRecipe", { enumerable: true, get: function
|
|
|
92
92
|
// than a method tree, so the recipe gates `send` and reads the command to tell inference from the
|
|
93
93
|
// rest of the API; it reports Bedrock's own token counters and no `cost`, because there is none on
|
|
94
94
|
// the response to report. See the recipe for what that means for a run that wanted to bill spend.
|
|
95
|
+
// Databricks model serving, as two recipes for two payers over one wire. `databricksRecipe` is a
|
|
96
|
+
// default, matched on the workspace URL: Foundation Model API traffic, billed to your Databricks
|
|
97
|
+
// account in DBUs. `databricksExternalRecipe` is the same wire reaching an endpoint configured
|
|
98
|
+
// with your own upstream key, where the upstream provider invoices - it is opt-in because the two
|
|
99
|
+
// are indistinguishable from the client and naming it is an assertion about your own workspace,
|
|
100
|
+
// not something the SDK detected. Neither reports a `cost`: Databricks states no price on any
|
|
101
|
+
// response, on either route. See the recipe header for what a wrong assertion costs.
|
|
102
|
+
var databricks_recipe_1 = require("./bindings/client/gateways/databricks/databricks-recipe");
|
|
103
|
+
Object.defineProperty(exports, "databricksRecipe", { enumerable: true, get: function () { return databricks_recipe_1.databricksRecipe; } });
|
|
104
|
+
Object.defineProperty(exports, "databricksExternalRecipe", { enumerable: true, get: function () { return databricks_recipe_1.databricksExternalRecipe; } });
|
|
95
105
|
var bedrock_recipe_1 = require("./bindings/client/gateways/bedrock/bedrock-recipe");
|
|
96
106
|
Object.defineProperty(exports, "bedrockRecipe", { enumerable: true, get: function () { return bedrock_recipe_1.bedrockRecipe; } });
|
|
97
107
|
// Built in as well. One method, no stream, and two counters - see the recipe for why a
|
|
98
108
|
// question-answering API has neither a cache counter nor a cost to report.
|
|
109
|
+
// Cloudflare Workers AI - a provider, not a gateway: Cloudflare runs these models on its own GPUs
|
|
110
|
+
// and invoices them, and `@cf/<author>/…` names who made the weights rather than who bills. A
|
|
111
|
+
// default, matched on `api.cloudflare.com`, covering both the OpenAI-compatible route and the
|
|
112
|
+
// `cloudflare` package's own `ai.run`. It reports tokens and no `cost` - Cloudflare bills in
|
|
113
|
+
// Neurons and publishes none of them on any response. See the recipe for the streamed `ai.run`
|
|
114
|
+
// limitation.
|
|
115
|
+
var cloudflare_recipe_1 = require("./bindings/client/gateways/cloudflare/cloudflare-recipe");
|
|
116
|
+
Object.defineProperty(exports, "cloudflareRecipe", { enumerable: true, get: function () { return cloudflare_recipe_1.cloudflareRecipe; } });
|
|
99
117
|
var typesafe_recipe_1 = require("./bindings/client/providers/typesafe/typesafe-recipe");
|
|
100
118
|
Object.defineProperty(exports, "typesafeRecipe", { enumerable: true, get: function () { return typesafe_recipe_1.typesafeRecipe; } });
|
|
101
119
|
Object.defineProperty(exports, "estimateFromState", { enumerable: true, get: function () { return typesafe_recipe_1.estimateFromState; } });
|
package/dist/internal.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export { noCustomer, overrideContext, resolveContext, isEnforced } from './core/
|
|
|
26
26
|
export type { ResolvedContext } from './core/context';
|
|
27
27
|
export { GovernanceSession } from './core/session';
|
|
28
28
|
export type { SessionDeps } from './core/session';
|
|
29
|
-
export { ANTHROPIC_METRICS, GEMINI_METRICS, MISTRAL_METRICS, MISTRAL_METRIC_NAME_MAP, OLLAMA_METRICS, TYPESAFE_METRICS, GATEWAY_METRICS, OPENAI_CHAT_METRICS, OPENAI_METRICS, OPENAI_RESPONSES_METRICS, METRIC_PATHS, metricName, metricNamesMatchPaths, pickMetrics, pickRenamedMetrics, addMetrics, addTokenUsage, fromFlatTokenUsage, fromGeminiUsage, fromLangChainUsage, fromMistralUsage, fromOllamaUsage, fromOpenAIUsage, fromResponsesUsage, observedMeasurement, } from './bindings/shared/token-usage';
|
|
29
|
+
export { ANTHROPIC_METRICS, GEMINI_METRICS, MISTRAL_METRICS, MISTRAL_METRIC_NAME_MAP, OLLAMA_METRICS, TYPESAFE_METRICS, GATEWAY_METRICS, DATABRICKS_METRICS, DATABRICKS_QUERY_METRIC_NAME_MAP, WORKERS_AI_METRICS, OPENAI_CHAT_METRICS, OPENAI_METRICS, OPENAI_RESPONSES_METRICS, METRIC_PATHS, metricName, metricNamesMatchPaths, pickMetrics, pickRenamedMetrics, addMetrics, addTokenUsage, fromFlatTokenUsage, fromGeminiUsage, fromLangChainUsage, fromMistralUsage, fromOllamaUsage, fromOpenAIUsage, fromResponsesUsage, observedMeasurement, } from './bindings/shared/token-usage';
|
|
30
30
|
export type { ObservedVendor } from './bindings/shared/token-usage';
|
|
31
31
|
export { DEFAULT_RECIPES } from './stigg-agent-sdk';
|
|
32
32
|
export { meterStream } from './bindings/shared/meter-stream';
|
package/dist/internal.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* can do from the public barrel, do from there instead.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.
|
|
14
|
+
exports.meterStream = exports.DEFAULT_RECIPES = exports.observedMeasurement = exports.fromResponsesUsage = exports.fromOpenAIUsage = exports.fromOllamaUsage = exports.fromMistralUsage = exports.fromLangChainUsage = exports.fromGeminiUsage = exports.fromFlatTokenUsage = exports.addTokenUsage = exports.addMetrics = exports.pickRenamedMetrics = exports.pickMetrics = exports.metricNamesMatchPaths = exports.metricName = exports.METRIC_PATHS = exports.OPENAI_RESPONSES_METRICS = exports.OPENAI_METRICS = exports.OPENAI_CHAT_METRICS = exports.WORKERS_AI_METRICS = exports.DATABRICKS_QUERY_METRIC_NAME_MAP = exports.DATABRICKS_METRICS = exports.GATEWAY_METRICS = exports.TYPESAFE_METRICS = exports.OLLAMA_METRICS = exports.MISTRAL_METRIC_NAME_MAP = exports.MISTRAL_METRICS = exports.GEMINI_METRICS = exports.ANTHROPIC_METRICS = exports.GovernanceSession = exports.isEnforced = exports.resolveContext = exports.overrideContext = exports.noCustomer = exports.prefixed = exports.asEngineError = exports.isMeterMismatchError = exports.isTransportFailure = exports.resolveConfig = exports.unenforcedDecision = exports.toDecision = exports.remedyFor = exports.policyDecision = exports.withDecisionText = exports.describeResolution = exports.RestGovernanceEngine = exports.NodeSdkEngine = exports.CompositeEngine = exports.createGovernanceEngine = void 0;
|
|
15
|
+
exports.eventNameFor = void 0;
|
|
15
16
|
// The engine seam's implementations. The interface itself is public - see `index.ts`.
|
|
16
17
|
var create_engine_1 = require("./engine/create-engine");
|
|
17
18
|
Object.defineProperty(exports, "createGovernanceEngine", { enumerable: true, get: function () { return create_engine_1.createGovernanceEngine; } });
|
|
@@ -58,6 +59,9 @@ Object.defineProperty(exports, "MISTRAL_METRIC_NAME_MAP", { enumerable: true, ge
|
|
|
58
59
|
Object.defineProperty(exports, "OLLAMA_METRICS", { enumerable: true, get: function () { return token_usage_1.OLLAMA_METRICS; } });
|
|
59
60
|
Object.defineProperty(exports, "TYPESAFE_METRICS", { enumerable: true, get: function () { return token_usage_1.TYPESAFE_METRICS; } });
|
|
60
61
|
Object.defineProperty(exports, "GATEWAY_METRICS", { enumerable: true, get: function () { return token_usage_1.GATEWAY_METRICS; } });
|
|
62
|
+
Object.defineProperty(exports, "DATABRICKS_METRICS", { enumerable: true, get: function () { return token_usage_1.DATABRICKS_METRICS; } });
|
|
63
|
+
Object.defineProperty(exports, "DATABRICKS_QUERY_METRIC_NAME_MAP", { enumerable: true, get: function () { return token_usage_1.DATABRICKS_QUERY_METRIC_NAME_MAP; } });
|
|
64
|
+
Object.defineProperty(exports, "WORKERS_AI_METRICS", { enumerable: true, get: function () { return token_usage_1.WORKERS_AI_METRICS; } });
|
|
61
65
|
Object.defineProperty(exports, "OPENAI_CHAT_METRICS", { enumerable: true, get: function () { return token_usage_1.OPENAI_CHAT_METRICS; } });
|
|
62
66
|
Object.defineProperty(exports, "OPENAI_METRICS", { enumerable: true, get: function () { return token_usage_1.OPENAI_METRICS; } });
|
|
63
67
|
Object.defineProperty(exports, "OPENAI_RESPONSES_METRICS", { enumerable: true, get: function () { return token_usage_1.OPENAI_RESPONSES_METRICS; } });
|
|
@@ -77,7 +77,7 @@ export declare const DEFAULT_RECIPES: ({
|
|
|
77
77
|
} | undefined;
|
|
78
78
|
attachStream: (stream: any, ingest: (measurement: import("./types").Measurement) => void) => void;
|
|
79
79
|
}[];
|
|
80
|
-
} | import("./bindings/client/core/types").ClientRecipe<"cached_tokens" | "prompt_tokens" | "completion_tokens" | "input_tokens" | "output_tokens" | "total_tokens" | "cost"> | {
|
|
80
|
+
} | import("./bindings/client/core/types").ClientRecipe<"cached_tokens" | "prompt_tokens" | "completion_tokens" | "input_tokens" | "output_tokens" | "total_tokens" | "cost"> | import("./bindings/client/core/types").ClientRecipe<"prompt_tokens" | "completion_tokens" | "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "cache_creation_input_tokens" | "total_tokens" | "reasoning_tokens"> | import("./bindings/client/core/types").ClientRecipe<"cached_tokens" | "prompt_tokens" | "completion_tokens" | "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "cache_creation_input_tokens" | "total_tokens"> | {
|
|
81
81
|
vendor: string;
|
|
82
82
|
metrics: readonly ["prompt_tokens", "completion_tokens", "cached_tokens", "total_tokens"];
|
|
83
83
|
totalTokensMetric: "total_tokens";
|
package/dist/stigg-agent-sdk.js
CHANGED
|
@@ -25,6 +25,8 @@ const dimensions_1 = require("./core/dimensions");
|
|
|
25
25
|
const anthropic_recipe_1 = require("./bindings/client/providers/anthropic/anthropic-recipe");
|
|
26
26
|
const gemini_recipe_1 = require("./bindings/client/providers/gemini/gemini-recipe");
|
|
27
27
|
const litellm_recipe_1 = require("./bindings/client/gateways/litellm/litellm-recipe");
|
|
28
|
+
const databricks_recipe_1 = require("./bindings/client/gateways/databricks/databricks-recipe");
|
|
29
|
+
const cloudflare_recipe_1 = require("./bindings/client/gateways/cloudflare/cloudflare-recipe");
|
|
28
30
|
const groq_recipe_1 = require("./bindings/client/providers/groq/groq-recipe");
|
|
29
31
|
const mistral_recipe_1 = require("./bindings/client/providers/mistral/mistral-recipe");
|
|
30
32
|
const ollama_recipe_1 = require("./bindings/client/providers/ollama/ollama-recipe");
|
|
@@ -55,9 +57,20 @@ const types_1 = require("./types");
|
|
|
55
57
|
* tokens is that mapper's business, and stays the caller's to decide.
|
|
56
58
|
*/
|
|
57
59
|
exports.DEFAULT_RECIPES = [
|
|
60
|
+
// First, and that position is load-bearing. Cloudflare AI Gateway forwards the upstream body
|
|
61
|
+
// untouched, so an `Anthropic` client pointed at it is an Anthropic client by shape and only the
|
|
62
|
+
// hostname says otherwise - and `anthropicRecipe` has no base-URL guard, so asked first it would
|
|
63
|
+
// claim that client and meter gateway traffic as direct Anthropic traffic. The same argument puts
|
|
64
|
+
// it ahead of `openAIRecipe` for its OpenAI-shaped routes and for Workers AI's compatible one.
|
|
65
|
+
(0, cloudflare_recipe_1.cloudflareRecipe)(),
|
|
58
66
|
anthropic_recipe_1.anthropicRecipe,
|
|
59
67
|
(0, openrouter_recipe_1.openrouterRecipe)(),
|
|
60
68
|
(0, litellm_recipe_1.litellmRecipe)(),
|
|
69
|
+
// The Databricks workspace route, matched on host *and* serving path - the host families alone
|
|
70
|
+
// are every Databricks API there is, most of which are not inference. Only the hosted recipe is
|
|
71
|
+
// here: `databricksExternalRecipe` covers the same wire reaching an endpoint billed by somebody
|
|
72
|
+
// else, which no matcher can tell apart, so it is named at `wrap` rather than guessed at.
|
|
73
|
+
(0, databricks_recipe_1.databricksRecipe)(),
|
|
61
74
|
// Ahead of `openAIRecipe`, like the gateways above and for the same reason: shape cannot tell a
|
|
62
75
|
// Groq client from any other OpenAI-compatible one, so the hostname decides, and the recipe that
|
|
63
76
|
// reads the hostname has to be asked first or the generic one claims the client.
|
package/dist/types.d.ts
CHANGED
|
@@ -454,6 +454,14 @@ export type ChatGatedPath = ['sendMessage'] | ['sendMessageStream'];
|
|
|
454
454
|
* `ollamaRecipe.matches` requires.
|
|
455
455
|
*/
|
|
456
456
|
export type OllamaGatedPath = ['chat'] | ['generate'];
|
|
457
|
+
/**
|
|
458
|
+
* Databricks' first-party client, whose whole surface is `query`. Out of {@link GatedPath} for the
|
|
459
|
+
* same reason as the two above, and more urgently: `query` is not merely a common method name, it
|
|
460
|
+
* is the one every database client in the ecosystem already owns. In the fallback list it would
|
|
461
|
+
* type `stiggContext` onto a `pg`, `mysql2`, Snowflake or BigQuery client - objects no recipe
|
|
462
|
+
* matches - and that context would be forwarded to the driver as an unknown field.
|
|
463
|
+
*/
|
|
464
|
+
export type DatabricksGatedPath = ['query'];
|
|
457
465
|
/**
|
|
458
466
|
* The paths to overlay when no recipe was named, given what the client actually looks like.
|
|
459
467
|
*
|
|
@@ -509,9 +517,35 @@ type GatedVendorShape = {
|
|
|
509
517
|
* the vendor as an unknown field. So anything the registry would claim first is excluded here,
|
|
510
518
|
* mirroring that precedence.
|
|
511
519
|
*/
|
|
512
|
-
export type ChatOf<T> = T extends GatedVendorShape ? GatedPath : T extends OllamaShape ? GatedPath | OllamaGatedPath : T extends {
|
|
520
|
+
export type ChatOf<T> = T extends GatedVendorShape ? GatedPath : T extends OllamaShape ? GatedPath | OllamaGatedPath : T extends DatabricksQueryShape ? GatedPath | DatabricksGatedPath : T extends {
|
|
513
521
|
getHistory: (...args: any[]) => any;
|
|
514
522
|
} ? GatedPath | ChatGatedPath : GatedPath;
|
|
523
|
+
/**
|
|
524
|
+
* What `databricksRecipe.matches` looks for, as near as a type can get - and the gap is the point.
|
|
525
|
+
*
|
|
526
|
+
* The runtime guard is `query` **plus** a `userAgent` naming the Databricks SDK. That second
|
|
527
|
+
* marker cannot be written here: `userAgent` is declared `private` in the client's `.d.ts`, and a
|
|
528
|
+
* `private` member makes a class non-assignable to any structural type naming it. So unlike
|
|
529
|
+
* {@link OllamaShape}, which mirrors its matcher exactly, this keys on the one thing the client
|
|
530
|
+
* does publish - the shape of what `query` resolves to.
|
|
531
|
+
*
|
|
532
|
+
* That still discriminates, which was checked rather than assumed: `QueryEndpointResponse` has no
|
|
533
|
+
* required property, so a target of all-optional fields would normally be a weak type that
|
|
534
|
+
* anything satisfies - but a `pg` or Snowflake result shares *none* of these names, and TypeScript's
|
|
535
|
+
* weak-type rule rejects exactly that case. `created?: bigint` is the field doing most of the work;
|
|
536
|
+
* nothing else in the ecosystem returns a bigint timestamp beside a `servedModelName`.
|
|
537
|
+
*
|
|
538
|
+
* The residual gap is a `query` typed as `Promise<any>`, which matches everything by construction.
|
|
539
|
+
* That costs a context offered on a method the runtime will decline to gate - the type is wider
|
|
540
|
+
* than the matcher, never narrower, so nothing is gated that was not typed.
|
|
541
|
+
*/
|
|
542
|
+
type DatabricksQueryShape = {
|
|
543
|
+
query: (...args: any[]) => Promise<{
|
|
544
|
+
servedModelName?: string | undefined;
|
|
545
|
+
predictions?: unknown;
|
|
546
|
+
created?: bigint | undefined;
|
|
547
|
+
}>;
|
|
548
|
+
};
|
|
515
549
|
/** What `ollamaRecipe.matches` looks for, as a type. Kept in step with it by hand. */
|
|
516
550
|
type OllamaShape = {
|
|
517
551
|
chat: (...args: any[]) => any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigg/agent-sdk-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Stigg agent SDK (JS) - run-lifecycle governance for agentic stacks, with examples per layer",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -40,12 +40,15 @@
|
|
|
40
40
|
"example-client-ollama": "tsx examples/client/ollama.ts",
|
|
41
41
|
"example-client-openrouter": "tsx examples/client/openrouter.ts",
|
|
42
42
|
"example-client-litellm": "tsx examples/client/litellm.ts",
|
|
43
|
+
"example-client-databricks": "tsx examples/client/databricks.ts",
|
|
43
44
|
"example-client-typesafe": "tsx examples/client/typesafe.ts",
|
|
44
45
|
"example-two-agents": "tsx examples/client/two-agents.ts",
|
|
45
46
|
"example-gateway": "tsx examples/gateway/openrouter.ts",
|
|
46
47
|
"example-gateway-openrouter": "tsx examples/gateway/openrouter.ts",
|
|
47
48
|
"example-gateway-litellm": "tsx examples/gateway/litellm.ts",
|
|
48
|
-
"example-gateway-
|
|
49
|
+
"example-gateway-cloudflare": "tsx examples/gateway/cloudflare.ts",
|
|
50
|
+
"example-gateway-bedrock": "tsx examples/gateway/bedrock.ts",
|
|
51
|
+
"example-gateway-databricks": "tsx examples/gateway/databricks.ts"
|
|
49
52
|
},
|
|
50
53
|
"nx": {
|
|
51
54
|
"includedScripts": [
|
|
@@ -68,10 +71,12 @@
|
|
|
68
71
|
"devDependencies": {
|
|
69
72
|
"@anthropic-ai/sdk": "^0.124.0",
|
|
70
73
|
"@aws-sdk/client-bedrock-runtime": "^3.1150.0",
|
|
74
|
+
"@databricks/sdk-modelservingquery": "0.25.0",
|
|
71
75
|
"@google/genai": "^2.28.0",
|
|
72
76
|
"@mistralai/mistralai": "^2.7.0",
|
|
73
77
|
"@openrouter/sdk": "^1.4.25",
|
|
74
78
|
"@typesafe-ai/sdk": "^0.6.0",
|
|
79
|
+
"cloudflare": "^7.3.0",
|
|
75
80
|
"dotenv": "^17.4.2",
|
|
76
81
|
"groq-sdk": "^1.6.0",
|
|
77
82
|
"ollama": "^0.6.4",
|