@hourslabs/domovoi 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 +262 -19
- package/dist/budget-tracker.d.ts +72 -0
- package/dist/budget-tracker.d.ts.map +1 -0
- package/dist/calibration/index.js.map +1 -1
- package/dist/context-storage.d.ts +44 -0
- package/dist/context-storage.d.ts.map +1 -0
- package/dist/engine/decide.d.ts +9 -0
- package/dist/engine/decide.d.ts.map +1 -1
- package/dist/engine/distribution.d.ts +1 -1
- package/dist/engine/distribution.d.ts.map +1 -1
- package/dist/engine/finalize.d.ts +7 -0
- package/dist/engine/finalize.d.ts.map +1 -1
- package/dist/errors.d.ts +18 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +256 -39
- package/dist/index.js.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/scope.d.ts +98 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/testing/distribution.d.ts +66 -0
- package/dist/testing/distribution.d.ts.map +1 -0
- package/dist/testing/index.d.ts +7 -3
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +136 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/tracer.d.ts +37 -0
- package/dist/tracer.d.ts.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,17 @@
|
|
|
7
7
|
* - `@hourslabs/domovoi/testing` — mockProvider for unit tests.
|
|
8
8
|
*/
|
|
9
9
|
import { memoryCache as memoryCacheFactory } from "./cache.js";
|
|
10
|
+
import { bind as bindFn, currentScope as currentScopeFn, scope as scopeFn } from "./scope.js";
|
|
10
11
|
import { boolean as booleanVerb } from "./verbs/boolean.js";
|
|
11
12
|
import { classifier as classifierFactory } from "./verbs/classifier.js";
|
|
12
13
|
import { classify as classifyVerb } from "./verbs/classify.js";
|
|
14
|
+
export type { BudgetMode, BudgetSnapshot, ScopeBudget } from "./budget-tracker.js";
|
|
13
15
|
export type { Cache, CacheStats, CacheWithStats } from "./cache.js";
|
|
14
|
-
export
|
|
16
|
+
export type { ContextStorage } from "./context-storage.js";
|
|
17
|
+
export { configureContextStorage, resetContextStorage } from "./context-storage.js";
|
|
18
|
+
export { BudgetExceededError, BudgetExhaustedError, ConfigError, DomovoiError, type ErrorCode, ProviderError, } from "./errors.js";
|
|
19
|
+
export type { ResolvedScope, ScopeOptions } from "./scope.js";
|
|
20
|
+
export type { AttributeValue, Span, Tracer } from "./tracer.js";
|
|
15
21
|
export type { Budget, Classified, Distribution, Filterable, Label, PromptTemplate, ProviderCapabilities, SerializableError, Thresholds, Uncertain, Unknown, UnknownVerdictCause, Verdict, VerdictMeta, } from "./types.js";
|
|
16
22
|
export type { BooleanOptions } from "./verbs/boolean.js";
|
|
17
23
|
export type { Classifier, ClassifierConfig } from "./verbs/classifier.js";
|
|
@@ -24,11 +30,20 @@ export { filter, isClassified, isUncertain, isUnknown, match } from "./verdict.j
|
|
|
24
30
|
* import { domovoi, isClassified } from "@hourslabs/domovoi";
|
|
25
31
|
* const v = await domovoi.classify(input, ["a","b","c"] as const);
|
|
26
32
|
* if (isClassified(v)) console.log(v.value);
|
|
33
|
+
*
|
|
34
|
+
* @example v0.2 ambient context
|
|
35
|
+
* await domovoi.scope({ budget: { tokens: 10_000 } }, async () => {
|
|
36
|
+
* const v = await domovoi.classify(input, ["a","b","c"]);
|
|
37
|
+
* // v0.2: classify call inherits budget enforcement from scope
|
|
38
|
+
* });
|
|
27
39
|
*/
|
|
28
40
|
export declare const domovoi: {
|
|
29
41
|
readonly classify: typeof classifyVerb;
|
|
30
42
|
readonly boolean: typeof booleanVerb;
|
|
31
43
|
readonly classifier: typeof classifierFactory;
|
|
32
44
|
readonly memoryCache: typeof memoryCacheFactory;
|
|
45
|
+
readonly scope: typeof scopeFn;
|
|
46
|
+
readonly bind: typeof bindFn;
|
|
47
|
+
readonly currentScope: typeof currentScopeFn;
|
|
33
48
|
};
|
|
34
49
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE/D,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,KAAK,SAAS,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,MAAM,EACN,UAAU,EACV,YAAY,EACZ,UAAU,EACV,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,OAAO,EACP,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEnF
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,IAAI,IAAI,MAAM,EAAE,YAAY,IAAI,cAAc,EAAE,KAAK,IAAI,OAAO,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE/D,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACnF,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,KAAK,SAAS,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EACV,MAAM,EACN,UAAU,EACV,YAAY,EACZ,UAAU,EACV,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,OAAO,EACP,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEnF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO;;;;;;;;CAQV,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
3
|
import OpenAI from 'openai';
|
|
3
4
|
import { get_encoding } from 'tiktoken';
|
|
4
5
|
|
|
@@ -158,6 +159,19 @@ var BudgetExhaustedError = class extends DomovoiError {
|
|
|
158
159
|
this.elapsedMs = options.elapsedMs;
|
|
159
160
|
}
|
|
160
161
|
};
|
|
162
|
+
var BudgetExceededError = class extends DomovoiError {
|
|
163
|
+
spent;
|
|
164
|
+
limit;
|
|
165
|
+
constructor(options) {
|
|
166
|
+
super(`Scope budget exceeded: ${options.spent} / ${options.limit} tokens`, {
|
|
167
|
+
code: "tokens_exceeded",
|
|
168
|
+
cause: options.cause
|
|
169
|
+
});
|
|
170
|
+
this.name = "BudgetExceededError";
|
|
171
|
+
this.spent = options.spent;
|
|
172
|
+
this.limit = options.limit;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
161
175
|
function canonicalizeProviderThrow(thrown) {
|
|
162
176
|
if (thrown instanceof DomovoiError) return thrown;
|
|
163
177
|
if (thrown instanceof Error) {
|
|
@@ -186,6 +200,109 @@ function serializeError(err) {
|
|
|
186
200
|
};
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
// src/budget-tracker.ts
|
|
204
|
+
var BudgetTracker = class _BudgetTracker {
|
|
205
|
+
constructor(limit, mode) {
|
|
206
|
+
this.limit = limit;
|
|
207
|
+
this.mode = mode;
|
|
208
|
+
}
|
|
209
|
+
limit;
|
|
210
|
+
mode;
|
|
211
|
+
spent = 0;
|
|
212
|
+
/**
|
|
213
|
+
* Build a tracker from a public `ScopeBudget`, or return `undefined` if
|
|
214
|
+
* the budget is absent / has no `tokens` field. Used during scope merge.
|
|
215
|
+
*
|
|
216
|
+
* Throws `ConfigError` if `tokens` is non-finite or non-positive — these
|
|
217
|
+
* are construction-time validation failures, not runtime budget exhaustion.
|
|
218
|
+
*/
|
|
219
|
+
static from(budget) {
|
|
220
|
+
if (budget?.tokens === void 0) return void 0;
|
|
221
|
+
if (!Number.isFinite(budget.tokens) || budget.tokens <= 0) {
|
|
222
|
+
throw new ConfigError(
|
|
223
|
+
`Scope budget tokens must be a finite positive number, got ${budget.tokens}`,
|
|
224
|
+
{ code: "invalid_scope_budget" }
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return new _BudgetTracker(budget.tokens, budget.onExceeded ?? "graceful");
|
|
228
|
+
}
|
|
229
|
+
/** Check whether the budget is already exhausted. Called pre-call. */
|
|
230
|
+
precheck() {
|
|
231
|
+
if (this.isExhausted()) {
|
|
232
|
+
return { ok: false, spent: this.spent, limit: this.limit };
|
|
233
|
+
}
|
|
234
|
+
return { ok: true };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Accumulate spend. Called post-call with the actual token cost.
|
|
238
|
+
* Negative or non-finite values are silently clamped to 0 — provider
|
|
239
|
+
* adapters occasionally return malformed token counts, and a budget
|
|
240
|
+
* tracker shouldn't panic on bad telemetry from upstream.
|
|
241
|
+
*/
|
|
242
|
+
charge(tokens) {
|
|
243
|
+
if (!Number.isFinite(tokens) || tokens <= 0) return;
|
|
244
|
+
this.spent += tokens;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Throw `BudgetExceededError` if mode is `"throw"` and budget is exhausted.
|
|
248
|
+
* Caller invokes after `charge()` to enforce hard-fail semantics; under
|
|
249
|
+
* `"graceful"` mode this is a no-op and the next `precheck()` returns
|
|
250
|
+
* `{ ok: false, ... }` for graceful Unknown construction.
|
|
251
|
+
*/
|
|
252
|
+
enforce() {
|
|
253
|
+
if (this.isExhausted() && this.mode === "throw") {
|
|
254
|
+
throw new BudgetExceededError({ spent: this.spent, limit: this.limit });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
snapshot() {
|
|
258
|
+
return { spent: this.spent, limit: this.limit, mode: this.mode };
|
|
259
|
+
}
|
|
260
|
+
isExhausted() {
|
|
261
|
+
return this.spent >= this.limit;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var storage = new AsyncLocalStorage();
|
|
265
|
+
function getContextStorage() {
|
|
266
|
+
return storage;
|
|
267
|
+
}
|
|
268
|
+
function configureContextStorage(custom) {
|
|
269
|
+
storage = custom;
|
|
270
|
+
}
|
|
271
|
+
function resetContextStorage() {
|
|
272
|
+
storage = new AsyncLocalStorage();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/scope.ts
|
|
276
|
+
function scope(opts, fn) {
|
|
277
|
+
const parent = currentScope();
|
|
278
|
+
const merged = mergeScopes(parent, opts);
|
|
279
|
+
return getContextStorage().run(merged, fn);
|
|
280
|
+
}
|
|
281
|
+
function currentScope() {
|
|
282
|
+
return getContextStorage().getStore();
|
|
283
|
+
}
|
|
284
|
+
function bind(fn) {
|
|
285
|
+
const captured = currentScope();
|
|
286
|
+
if (!captured) return fn;
|
|
287
|
+
const storage2 = getContextStorage();
|
|
288
|
+
return ((...args) => storage2.run(captured, () => fn(...args)));
|
|
289
|
+
}
|
|
290
|
+
function mergeScopes(parent, child) {
|
|
291
|
+
const signal = mergeSignals(parent?.signal, child.signal);
|
|
292
|
+
const tracer = child.tracer ?? parent?.tracer;
|
|
293
|
+
const budgetTracker = child.budget ? BudgetTracker.from(child.budget) : parent?.budgetTracker;
|
|
294
|
+
return {
|
|
295
|
+
...signal ? { signal } : {},
|
|
296
|
+
...tracer ? { tracer } : {},
|
|
297
|
+
...budgetTracker ? { budgetTracker } : {}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function mergeSignals(a, b) {
|
|
301
|
+
if (!a) return b;
|
|
302
|
+
if (!b) return a;
|
|
303
|
+
return AbortSignal.any([a, b]);
|
|
304
|
+
}
|
|
305
|
+
|
|
189
306
|
// src/calibration/index.ts
|
|
190
307
|
var identity = {
|
|
191
308
|
kind: "identity",
|
|
@@ -380,6 +497,21 @@ function validateClassifierConfig(input) {
|
|
|
380
497
|
}
|
|
381
498
|
}
|
|
382
499
|
|
|
500
|
+
// src/tracer.ts
|
|
501
|
+
var noopSpan = {
|
|
502
|
+
setAttribute: () => {
|
|
503
|
+
},
|
|
504
|
+
recordException: () => {
|
|
505
|
+
},
|
|
506
|
+
setStatus: () => {
|
|
507
|
+
},
|
|
508
|
+
end: () => {
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
var noopTracer = {
|
|
512
|
+
startSpan: () => noopSpan
|
|
513
|
+
};
|
|
514
|
+
|
|
383
515
|
// src/engine/meta.ts
|
|
384
516
|
function makeMetaBuilder() {
|
|
385
517
|
return {
|
|
@@ -433,11 +565,11 @@ function buildCancelledVerdict(meta, reason, provider) {
|
|
|
433
565
|
meta: buildMetaForFailure(meta, provider)
|
|
434
566
|
};
|
|
435
567
|
}
|
|
436
|
-
function buildBudgetExhaustedVerdict(meta,
|
|
568
|
+
function buildBudgetExhaustedVerdict(meta, scope2, policy, provider) {
|
|
437
569
|
if (policy === "throw") return void 0;
|
|
438
570
|
return {
|
|
439
571
|
kind: "unknown",
|
|
440
|
-
reason: { type: "budget_exhausted", scope },
|
|
572
|
+
reason: { type: "budget_exhausted", scope: scope2 },
|
|
441
573
|
meta: buildMetaForFailure(meta, provider)
|
|
442
574
|
};
|
|
443
575
|
}
|
|
@@ -469,9 +601,10 @@ function computeProviderCacheKey(provider, formattedInput, config) {
|
|
|
469
601
|
formattedInput
|
|
470
602
|
});
|
|
471
603
|
}
|
|
472
|
-
function
|
|
604
|
+
function mergeSignals2(userSignal, timeoutSignal, scopeSignal) {
|
|
473
605
|
const signals = [timeoutSignal];
|
|
474
|
-
if (userSignal
|
|
606
|
+
if (userSignal) signals.push(userSignal);
|
|
607
|
+
if (scopeSignal) signals.push(scopeSignal);
|
|
475
608
|
return AbortSignal.any(signals);
|
|
476
609
|
}
|
|
477
610
|
async function loadDistribution(provider, formattedInput, config, meta, signal, cacheKey) {
|
|
@@ -583,6 +716,16 @@ function makeCancelledFromMeta(meta, reason) {
|
|
|
583
716
|
meta: buildMetaForFailure(meta)
|
|
584
717
|
};
|
|
585
718
|
}
|
|
719
|
+
function makeBudgetExceededVerdict(meta, spent, limit, mode) {
|
|
720
|
+
if (mode === "throw") {
|
|
721
|
+
throw new BudgetExceededError({ spent, limit });
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
kind: "unknown",
|
|
725
|
+
reason: { type: "budget_exceeded", spent, limit },
|
|
726
|
+
meta: buildMetaForFailure(meta)
|
|
727
|
+
};
|
|
728
|
+
}
|
|
586
729
|
function finalizeChainExhausted(meta, lastCalibrated, attempts, config) {
|
|
587
730
|
const allErrored = lastCalibrated === void 0 && meta.providerErrors.length > 0 && meta.providerErrors.length === meta.providersAttempted.length;
|
|
588
731
|
if (allErrored) {
|
|
@@ -666,7 +809,8 @@ function applyMultiClassRule(top, second, t) {
|
|
|
666
809
|
// src/engine/decide.ts
|
|
667
810
|
async function decide(formattedInput, config, signal) {
|
|
668
811
|
const meta = makeMetaBuilder();
|
|
669
|
-
const
|
|
812
|
+
const scope2 = currentScope();
|
|
813
|
+
const preAbort = firstAbortReason(signal, scope2?.signal);
|
|
670
814
|
if (preAbort !== void 0) {
|
|
671
815
|
return makeCancelledFromMeta(meta, preAbort);
|
|
672
816
|
}
|
|
@@ -687,7 +831,20 @@ async function decide(formattedInput, config, signal) {
|
|
|
687
831
|
fireAndForget(config.hooks?.onResult, chainBudgetVerdict);
|
|
688
832
|
return chainBudgetVerdict;
|
|
689
833
|
}
|
|
690
|
-
|
|
834
|
+
if (scope2?.budgetTracker) {
|
|
835
|
+
const pre = scope2.budgetTracker.precheck();
|
|
836
|
+
if (!pre.ok) {
|
|
837
|
+
const verdict = makeBudgetExceededVerdict(
|
|
838
|
+
meta,
|
|
839
|
+
pre.spent,
|
|
840
|
+
pre.limit,
|
|
841
|
+
scope2.budgetTracker.mode
|
|
842
|
+
);
|
|
843
|
+
fireAndForget(config.hooks?.onResult, verdict);
|
|
844
|
+
return verdict;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const midAbort = firstAbortReason(signal, scope2?.signal);
|
|
691
848
|
if (midAbort !== void 0) {
|
|
692
849
|
const verdict = buildCancelledVerdict(meta, midAbort, provider);
|
|
693
850
|
fireAndForget(config.hooks?.onResult, verdict);
|
|
@@ -703,7 +860,8 @@ async function decide(formattedInput, config, signal) {
|
|
|
703
860
|
i,
|
|
704
861
|
chainStartMs,
|
|
705
862
|
limits.perCallTimeoutMs,
|
|
706
|
-
isLastProvider
|
|
863
|
+
isLastProvider,
|
|
864
|
+
scope2
|
|
707
865
|
);
|
|
708
866
|
if (outcome.kind === "verdict") {
|
|
709
867
|
fireAndForget(config.hooks?.onResult, outcome.verdict);
|
|
@@ -725,40 +883,84 @@ function computeLimits(config) {
|
|
|
725
883
|
maxCalls: config.budget?.maxCalls ?? config.providers.length
|
|
726
884
|
};
|
|
727
885
|
}
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
let distribution;
|
|
733
|
-
try {
|
|
734
|
-
distribution = await loadDistribution(
|
|
735
|
-
provider,
|
|
736
|
-
formattedInput,
|
|
737
|
-
config,
|
|
738
|
-
meta,
|
|
739
|
-
mergedSignal,
|
|
740
|
-
cacheKey
|
|
741
|
-
);
|
|
742
|
-
} catch (err) {
|
|
743
|
-
return handleDistributionError(
|
|
744
|
-
err,
|
|
745
|
-
provider,
|
|
746
|
-
meta,
|
|
747
|
-
config,
|
|
748
|
-
userSignal,
|
|
749
|
-
timeoutSignal,
|
|
750
|
-
index,
|
|
751
|
-
chainStartMs,
|
|
752
|
-
cacheKey
|
|
753
|
-
);
|
|
886
|
+
function firstAbortReason(...signals) {
|
|
887
|
+
for (const s of signals) {
|
|
888
|
+
const reason = abortReason(s);
|
|
889
|
+
if (reason !== void 0) return reason;
|
|
754
890
|
}
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
function estimateInputTokens(formattedInput) {
|
|
894
|
+
return Math.ceil(formattedInput.length / 4);
|
|
895
|
+
}
|
|
896
|
+
function estimateOutputTokens() {
|
|
897
|
+
return 20;
|
|
898
|
+
}
|
|
899
|
+
async function attemptProvider(provider, formattedInput, config, meta, userSignal, index, chainStartMs, perCallTimeoutMs, isLastProvider, scope2) {
|
|
900
|
+
const tracer = scope2?.tracer ?? noopTracer;
|
|
901
|
+
const span = tracer.startSpan(`chat ${provider.id}`, {
|
|
902
|
+
"gen_ai.provider.name": provider.id,
|
|
903
|
+
"gen_ai.operation.name": "chat",
|
|
904
|
+
"gen_ai.request.model": provider.modelId,
|
|
905
|
+
"domovoi.label_space": [...config.space]
|
|
906
|
+
});
|
|
755
907
|
try {
|
|
756
|
-
|
|
908
|
+
const cacheKey = computeProviderCacheKey(provider, formattedInput, config);
|
|
909
|
+
const timeoutSignal = AbortSignal.timeout(perCallTimeoutMs);
|
|
910
|
+
const mergedSignal = mergeSignals2(userSignal, timeoutSignal, scope2?.signal);
|
|
911
|
+
let distribution;
|
|
912
|
+
try {
|
|
913
|
+
distribution = await loadDistribution(
|
|
914
|
+
provider,
|
|
915
|
+
formattedInput,
|
|
916
|
+
config,
|
|
917
|
+
meta,
|
|
918
|
+
mergedSignal,
|
|
919
|
+
cacheKey
|
|
920
|
+
);
|
|
921
|
+
} catch (err) {
|
|
922
|
+
span.recordException(err);
|
|
923
|
+
span.setStatus("error");
|
|
924
|
+
return handleDistributionError(
|
|
925
|
+
err,
|
|
926
|
+
provider,
|
|
927
|
+
meta,
|
|
928
|
+
config,
|
|
929
|
+
userSignal,
|
|
930
|
+
timeoutSignal,
|
|
931
|
+
index,
|
|
932
|
+
chainStartMs,
|
|
933
|
+
cacheKey
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
span.setAttribute("domovoi.cache.hit", meta.cacheHit);
|
|
937
|
+
if (!meta.cacheHit) {
|
|
938
|
+
const inputTokens = estimateInputTokens(formattedInput);
|
|
939
|
+
const outputTokens = estimateOutputTokens();
|
|
940
|
+
span.setAttribute("gen_ai.usage.input_tokens", inputTokens);
|
|
941
|
+
span.setAttribute("gen_ai.usage.output_tokens", outputTokens);
|
|
942
|
+
scope2?.budgetTracker?.charge(inputTokens + outputTokens);
|
|
943
|
+
scope2?.budgetTracker?.enforce();
|
|
944
|
+
}
|
|
945
|
+
try {
|
|
946
|
+
validateDistribution(distribution, config.space);
|
|
947
|
+
} catch (err) {
|
|
948
|
+
span.recordException(err);
|
|
949
|
+
span.setStatus("error");
|
|
950
|
+
return recordValidationError(err, provider, meta, config, index, cacheKey);
|
|
951
|
+
}
|
|
952
|
+
const calibrated = config.calibrator.apply(distribution);
|
|
953
|
+
const result = applyThresholds(calibrated, config.thresholds, config.space);
|
|
954
|
+
return finalizeOutcome(result, calibrated, provider, meta, isLastProvider, span);
|
|
757
955
|
} catch (err) {
|
|
758
|
-
|
|
956
|
+
span.recordException(err);
|
|
957
|
+
span.setStatus("error");
|
|
958
|
+
throw err;
|
|
959
|
+
} finally {
|
|
960
|
+
span.end();
|
|
759
961
|
}
|
|
760
|
-
|
|
761
|
-
|
|
962
|
+
}
|
|
963
|
+
function finalizeOutcome(result, calibrated, provider, meta, isLastProvider, span) {
|
|
762
964
|
if (result.kind === "classified") {
|
|
763
965
|
const verdict = {
|
|
764
966
|
kind: "classified",
|
|
@@ -766,6 +968,10 @@ async function attemptProvider(provider, formattedInput, config, meta, userSigna
|
|
|
766
968
|
probability: result.probability,
|
|
767
969
|
meta: buildMeta(meta, provider)
|
|
768
970
|
};
|
|
971
|
+
span.setAttribute("domovoi.verdict.kind", verdict.kind);
|
|
972
|
+
span.setAttribute("domovoi.verdict.value", verdict.value);
|
|
973
|
+
span.setAttribute("domovoi.verdict.probability", verdict.probability);
|
|
974
|
+
span.setStatus("ok");
|
|
769
975
|
return { kind: "verdict", verdict };
|
|
770
976
|
}
|
|
771
977
|
if (result.kind === "out_of_distribution") {
|
|
@@ -779,6 +985,8 @@ async function attemptProvider(provider, formattedInput, config, meta, userSigna
|
|
|
779
985
|
},
|
|
780
986
|
meta: buildMeta(meta, provider)
|
|
781
987
|
};
|
|
988
|
+
span.setAttribute("domovoi.verdict.kind", verdict.kind);
|
|
989
|
+
span.setStatus("ok");
|
|
782
990
|
return { kind: "verdict", verdict };
|
|
783
991
|
}
|
|
784
992
|
if (isLastProvider) {
|
|
@@ -790,8 +998,13 @@ async function attemptProvider(provider, formattedInput, config, meta, userSigna
|
|
|
790
998
|
distribution: calibrated,
|
|
791
999
|
meta: buildMeta(meta, provider)
|
|
792
1000
|
};
|
|
1001
|
+
span.setAttribute("domovoi.verdict.kind", verdict.kind);
|
|
1002
|
+
span.setAttribute("domovoi.verdict.probability", verdict.probability);
|
|
1003
|
+
span.setStatus("ok");
|
|
793
1004
|
return { kind: "lastUncertain", verdict, calibrated };
|
|
794
1005
|
}
|
|
1006
|
+
span.setAttribute("domovoi.verdict.kind", "continue");
|
|
1007
|
+
span.setStatus("ok");
|
|
795
1008
|
return { kind: "continue", calibrated };
|
|
796
1009
|
}
|
|
797
1010
|
var cl100kSingleton;
|
|
@@ -1126,6 +1339,7 @@ function convertCause(cause) {
|
|
|
1126
1339
|
case "predicate_rejected":
|
|
1127
1340
|
case "provider_failure":
|
|
1128
1341
|
case "budget_exhausted":
|
|
1342
|
+
case "budget_exceeded":
|
|
1129
1343
|
case "cancelled":
|
|
1130
1344
|
return cause;
|
|
1131
1345
|
}
|
|
@@ -1255,9 +1469,12 @@ var domovoi = {
|
|
|
1255
1469
|
classify,
|
|
1256
1470
|
boolean,
|
|
1257
1471
|
classifier,
|
|
1258
|
-
memoryCache
|
|
1472
|
+
memoryCache,
|
|
1473
|
+
scope,
|
|
1474
|
+
bind,
|
|
1475
|
+
currentScope
|
|
1259
1476
|
};
|
|
1260
1477
|
|
|
1261
|
-
export { BudgetExhaustedError, ConfigError, DomovoiError, ProviderError, domovoi, filter, isClassified, isUncertain, isUnknown, match };
|
|
1478
|
+
export { BudgetExceededError, BudgetExhaustedError, ConfigError, DomovoiError, ProviderError, configureContextStorage, domovoi, filter, isClassified, isUncertain, isUnknown, match, resetContextStorage };
|
|
1262
1479
|
//# sourceMappingURL=index.js.map
|
|
1263
1480
|
//# sourceMappingURL=index.js.map
|