@debriefer/core 2.0.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 +86 -0
- package/dist/__tests__/base-source.test.d.ts +2 -0
- package/dist/__tests__/base-source.test.d.ts.map +1 -0
- package/dist/__tests__/base-source.test.js +333 -0
- package/dist/__tests__/base-source.test.js.map +1 -0
- package/dist/__tests__/batch-runner.test.d.ts +2 -0
- package/dist/__tests__/batch-runner.test.d.ts.map +1 -0
- package/dist/__tests__/batch-runner.test.js +217 -0
- package/dist/__tests__/batch-runner.test.js.map +1 -0
- package/dist/__tests__/cache.test.d.ts +2 -0
- package/dist/__tests__/cache.test.d.ts.map +1 -0
- package/dist/__tests__/cache.test.js +127 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/confidence.test.d.ts +2 -0
- package/dist/__tests__/confidence.test.d.ts.map +1 -0
- package/dist/__tests__/confidence.test.js +81 -0
- package/dist/__tests__/confidence.test.js.map +1 -0
- package/dist/__tests__/cost-tracker.test.d.ts +2 -0
- package/dist/__tests__/cost-tracker.test.d.ts.map +1 -0
- package/dist/__tests__/cost-tracker.test.js +149 -0
- package/dist/__tests__/cost-tracker.test.js.map +1 -0
- package/dist/__tests__/orchestrator.test.d.ts +2 -0
- package/dist/__tests__/orchestrator.test.d.ts.map +1 -0
- package/dist/__tests__/orchestrator.test.js +751 -0
- package/dist/__tests__/orchestrator.test.js.map +1 -0
- package/dist/__tests__/rate-limiter.test.d.ts +2 -0
- package/dist/__tests__/rate-limiter.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limiter.test.js +83 -0
- package/dist/__tests__/rate-limiter.test.js.map +1 -0
- package/dist/__tests__/reliability.test.d.ts +2 -0
- package/dist/__tests__/reliability.test.d.ts.map +1 -0
- package/dist/__tests__/reliability.test.js +207 -0
- package/dist/__tests__/reliability.test.js.map +1 -0
- package/dist/__tests__/synthesizer.test.d.ts +2 -0
- package/dist/__tests__/synthesizer.test.d.ts.map +1 -0
- package/dist/__tests__/synthesizer.test.js +50 -0
- package/dist/__tests__/synthesizer.test.js.map +1 -0
- package/dist/__tests__/telemetry.test.d.ts +2 -0
- package/dist/__tests__/telemetry.test.d.ts.map +1 -0
- package/dist/__tests__/telemetry.test.js +81 -0
- package/dist/__tests__/telemetry.test.js.map +1 -0
- package/dist/__tests__/types.test.d.ts +2 -0
- package/dist/__tests__/types.test.d.ts.map +1 -0
- package/dist/__tests__/types.test.js +708 -0
- package/dist/__tests__/types.test.js.map +1 -0
- package/dist/base-source.d.ts +91 -0
- package/dist/base-source.d.ts.map +1 -0
- package/dist/base-source.js +144 -0
- package/dist/base-source.js.map +1 -0
- package/dist/batch-runner.d.ts +40 -0
- package/dist/batch-runner.d.ts.map +1 -0
- package/dist/batch-runner.js +65 -0
- package/dist/batch-runner.js.map +1 -0
- package/dist/cache/in-memory.d.ts +26 -0
- package/dist/cache/in-memory.d.ts.map +1 -0
- package/dist/cache/in-memory.js +51 -0
- package/dist/cache/in-memory.js.map +1 -0
- package/dist/confidence.d.ts +13 -0
- package/dist/confidence.d.ts.map +1 -0
- package/dist/confidence.js +29 -0
- package/dist/confidence.js.map +1 -0
- package/dist/cost-tracker.d.ts +37 -0
- package/dist/cost-tracker.d.ts.map +1 -0
- package/dist/cost-tracker.js +62 -0
- package/dist/cost-tracker.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator.d.ts +92 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +373 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/rate-limiter.d.ts +31 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +79 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/reliability.d.ts +49 -0
- package/dist/reliability.d.ts.map +1 -0
- package/dist/reliability.js +67 -0
- package/dist/reliability.js.map +1 -0
- package/dist/synthesizer.d.ts +31 -0
- package/dist/synthesizer.d.ts.map +1 -0
- package/dist/synthesizer.js +47 -0
- package/dist/synthesizer.js.map +1 -0
- package/dist/telemetry/console.d.ts +7 -0
- package/dist/telemetry/console.d.ts.map +1 -0
- package/dist/telemetry/console.js +21 -0
- package/dist/telemetry/console.js.map +1 -0
- package/dist/telemetry/noop.d.ts +7 -0
- package/dist/telemetry/noop.d.ts.map +1 -0
- package/dist/telemetry/noop.js +12 -0
- package/dist/telemetry/noop.js.map +1 -0
- package/dist/types.d.ts +417 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +102 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BatchCostTracker — tracks costs across a batch enrichment run.
|
|
3
|
+
*
|
|
4
|
+
* Supports per-subject cost limits (e.g., maxCostPerSubject) and a global
|
|
5
|
+
* total cost limit. Also records cost-by-source for reporting.
|
|
6
|
+
*/
|
|
7
|
+
export class BatchCostTracker {
|
|
8
|
+
totalCost = 0;
|
|
9
|
+
subjectCosts = new Map();
|
|
10
|
+
costBySource = {};
|
|
11
|
+
maxTotalCost;
|
|
12
|
+
maxCostPerSubject;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.maxTotalCost = options?.maxTotalCost;
|
|
15
|
+
this.maxCostPerSubject = options?.maxCostPerSubject;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add cost to a subject's running total.
|
|
19
|
+
* Returns whether any limit (total or per-subject) is now exceeded.
|
|
20
|
+
*/
|
|
21
|
+
addSubjectCost(subjectId, cost) {
|
|
22
|
+
this.totalCost += cost;
|
|
23
|
+
const current = this.subjectCosts.get(subjectId) ?? 0;
|
|
24
|
+
this.subjectCosts.set(subjectId, current + cost);
|
|
25
|
+
return this.isTotalLimitExceeded() || this.isSubjectLimitExceeded(subjectId);
|
|
26
|
+
}
|
|
27
|
+
/** Track cost by source type for reporting. */
|
|
28
|
+
addSourceCost(sourceType, cost) {
|
|
29
|
+
this.costBySource[sourceType] = (this.costBySource[sourceType] ?? 0) + cost;
|
|
30
|
+
}
|
|
31
|
+
/** Get total cost across all subjects. */
|
|
32
|
+
getTotalCost() {
|
|
33
|
+
return this.totalCost;
|
|
34
|
+
}
|
|
35
|
+
/** Get cost for a specific subject. */
|
|
36
|
+
getSubjectCost(subjectId) {
|
|
37
|
+
return this.subjectCosts.get(subjectId) ?? 0;
|
|
38
|
+
}
|
|
39
|
+
/** Check if total limit is exceeded. Returns false when no limit is set. */
|
|
40
|
+
isTotalLimitExceeded() {
|
|
41
|
+
if (this.maxTotalCost === undefined)
|
|
42
|
+
return false;
|
|
43
|
+
return this.totalCost >= this.maxTotalCost;
|
|
44
|
+
}
|
|
45
|
+
/** Check if a specific subject's limit is exceeded. Returns false when no limit is set. */
|
|
46
|
+
isSubjectLimitExceeded(subjectId) {
|
|
47
|
+
if (this.maxCostPerSubject === undefined)
|
|
48
|
+
return false;
|
|
49
|
+
return this.getSubjectCost(subjectId) >= this.maxCostPerSubject;
|
|
50
|
+
}
|
|
51
|
+
/** Get cost breakdown by source type. Returns a shallow copy. */
|
|
52
|
+
getCostBySource() {
|
|
53
|
+
return { ...this.costBySource };
|
|
54
|
+
}
|
|
55
|
+
/** Reset all tracking state. */
|
|
56
|
+
reset() {
|
|
57
|
+
this.totalCost = 0;
|
|
58
|
+
this.subjectCosts.clear();
|
|
59
|
+
this.costBySource = {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=cost-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-tracker.js","sourceRoot":"","sources":["../src/cost-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAAG,CAAC,CAAA;IACb,YAAY,GAAG,IAAI,GAAG,EAA2B,CAAA;IACjD,YAAY,GAA2B,EAAE,CAAA;IAEhC,YAAY,CAAoB;IAChC,iBAAiB,CAAoB;IAEtD,YAAY,OAA+D;QACzE,IAAI,CAAC,YAAY,GAAG,OAAO,EAAE,YAAY,CAAA;QACzC,IAAI,CAAC,iBAAiB,GAAG,OAAO,EAAE,iBAAiB,CAAA;IACrD,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,SAA0B,EAAE,IAAY;QACrD,IAAI,CAAC,SAAS,IAAI,IAAI,CAAA;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC,CAAA;QAChD,OAAO,IAAI,CAAC,oBAAoB,EAAE,IAAI,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAA;IAC9E,CAAC;IAED,+CAA+C;IAC/C,aAAa,CAAC,UAAkB,EAAE,IAAY;QAC5C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAA;IAC7E,CAAC;IAED,0CAA0C;IAC1C,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,uCAAuC;IACvC,cAAc,CAAC,SAA0B;QACvC,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC;IAED,4EAA4E;IAC5E,oBAAoB;QAClB,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACjD,OAAO,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY,CAAA;IAC5C,CAAC;IAED,2FAA2F;IAC3F,sBAAsB,CAAC,SAA0B;QAC/C,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACtD,OAAO,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAA;IACjE,CAAC;IAED,iEAAiE;IACjE,eAAe;QACb,OAAO,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;IACjC,CAAC;IAED,gCAAgC;IAChC,KAAK;QACH,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;QAClB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAA;QACzB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;IACxB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debriefer — Multi-source research orchestration engine.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates data sources with Wikipedia RSP-based reliability scoring,
|
|
5
|
+
* phased execution with early stopping, per-query cost control, and pluggable synthesis.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { ResearchOrchestrator } from "./orchestrator.js";
|
|
10
|
+
export { BaseResearchSource } from "./base-source.js";
|
|
11
|
+
export type { BaseSourceOptions } from "./base-source.js";
|
|
12
|
+
export { NoopSynthesizer, stripMarkdownCodeFences } from "./synthesizer.js";
|
|
13
|
+
export { ReliabilityTier, RELIABILITY_SCORES, getReliabilityScore, meetsReliabilityThreshold, } from "./reliability.js";
|
|
14
|
+
export { SourceRateLimiter } from "./rate-limiter.js";
|
|
15
|
+
export type { RateLimiterStats } from "./rate-limiter.js";
|
|
16
|
+
export { BatchCostTracker } from "./cost-tracker.js";
|
|
17
|
+
export { ParallelBatchRunner } from "./batch-runner.js";
|
|
18
|
+
export type { BatchProgress, BatchResult, ParallelBatchRunnerOptions } from "./batch-runner.js";
|
|
19
|
+
export { calculateConfidence } from "./confidence.js";
|
|
20
|
+
export { InMemoryCache } from "./cache/in-memory.js";
|
|
21
|
+
export { ConsoleTelemetry } from "./telemetry/console.js";
|
|
22
|
+
export { NoopTelemetry } from "./telemetry/noop.js";
|
|
23
|
+
export type { ResearchSubject, RawFinding, ScoredFinding, MinimalSource, SourcePhaseGroup, SynthesisOptions, SynthesisResult, Synthesizer, DebriefResult, ResearchConfig, CacheProvider, TelemetryProvider, TelemetrySpan, BatchProgressStats, BatchStats, LifecycleHooks, } from "./types.js";
|
|
24
|
+
export { CostLimitExceededError, SourceTimeoutError, SourceAccessBlockedError } from "./types.js";
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGzD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAG3E,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAGrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGnD,YAAY,EACV,eAAe,EACf,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debriefer — Multi-source research orchestration engine.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates data sources with Wikipedia RSP-based reliability scoring,
|
|
5
|
+
* phased execution with early stopping, per-query cost control, and pluggable synthesis.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
// Core engine
|
|
10
|
+
export { ResearchOrchestrator } from "./orchestrator.js";
|
|
11
|
+
export { BaseResearchSource } from "./base-source.js";
|
|
12
|
+
// Synthesis (ClaudeSynthesizer moved to @debriefer/ai)
|
|
13
|
+
export { NoopSynthesizer, stripMarkdownCodeFences } from "./synthesizer.js";
|
|
14
|
+
// Reliability scoring
|
|
15
|
+
export { ReliabilityTier, RELIABILITY_SCORES, getReliabilityScore, meetsReliabilityThreshold, } from "./reliability.js";
|
|
16
|
+
// Infrastructure
|
|
17
|
+
export { SourceRateLimiter } from "./rate-limiter.js";
|
|
18
|
+
export { BatchCostTracker } from "./cost-tracker.js";
|
|
19
|
+
export { ParallelBatchRunner } from "./batch-runner.js";
|
|
20
|
+
export { calculateConfidence } from "./confidence.js";
|
|
21
|
+
// Cache implementations
|
|
22
|
+
export { InMemoryCache } from "./cache/in-memory.js";
|
|
23
|
+
// Telemetry implementations
|
|
24
|
+
export { ConsoleTelemetry } from "./telemetry/console.js";
|
|
25
|
+
export { NoopTelemetry } from "./telemetry/noop.js";
|
|
26
|
+
// Error types
|
|
27
|
+
export { CostLimitExceededError, SourceTimeoutError, SourceAccessBlockedError } from "./types.js";
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc;AACd,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAGrD,uDAAuD;AACvD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE3E,sBAAsB;AACtB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAA;AAEzB,iBAAiB;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,wBAAwB;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,4BAA4B;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAsBnD,cAAc;AACd,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research Orchestrator — the core engine that wires sources, phases,
|
|
3
|
+
* rate limiting, cost tracking, and synthesis into a complete pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Two entry points:
|
|
6
|
+
* - `debrief(subject)` — research a single subject through all phases
|
|
7
|
+
* - `debriefBatch(subjects, hooks)` — research multiple subjects with
|
|
8
|
+
* concurrency, cost limits, and lifecycle hooks
|
|
9
|
+
*
|
|
10
|
+
* Algorithm (per subject):
|
|
11
|
+
* 1. For each phase in order:
|
|
12
|
+
* a. Filter to available sources (isAvailable())
|
|
13
|
+
* b. Execute sources — concurrently via Promise.allSettled() by default,
|
|
14
|
+
* or sequentially (stop at first success) when phase.sequential is true
|
|
15
|
+
* c. Collect successful findings, tag with reliability info → ScoredFinding
|
|
16
|
+
* d. Check early-stop: do we have earlyStopThreshold+ high-quality families?
|
|
17
|
+
* e. Check per-subject cost limit
|
|
18
|
+
* 2. Synthesize all accumulated findings via the injected Synthesizer
|
|
19
|
+
* 3. Return DebriefResult
|
|
20
|
+
*/
|
|
21
|
+
import type { ResearchSubject, ResearchConfig, DebriefResult, SourcePhaseGroup, Synthesizer, LifecycleHooks } from "./types.js";
|
|
22
|
+
/**
|
|
23
|
+
* Generic research orchestrator that coordinates source phases, rate limiting,
|
|
24
|
+
* cost tracking, and AI synthesis into a complete research pipeline.
|
|
25
|
+
*
|
|
26
|
+
* @typeParam TSubject - The research subject type (extends ResearchSubject)
|
|
27
|
+
* @typeParam TOutput - The structured output type produced by synthesis
|
|
28
|
+
*/
|
|
29
|
+
export declare class ResearchOrchestrator<TSubject extends ResearchSubject, TOutput> {
|
|
30
|
+
private phases;
|
|
31
|
+
private synthesizer;
|
|
32
|
+
private config;
|
|
33
|
+
private rateLimiter;
|
|
34
|
+
private telemetry;
|
|
35
|
+
constructor(phases: SourcePhaseGroup<TSubject>[], synthesizer: Synthesizer<TSubject, TOutput>, config?: ResearchConfig);
|
|
36
|
+
/**
|
|
37
|
+
* Research a single subject through all configured phases.
|
|
38
|
+
*
|
|
39
|
+
* Iterates through phases sequentially. Within each phase, sources run
|
|
40
|
+
* concurrently via Promise.allSettled by default, or sequentially (stopping
|
|
41
|
+
* at first success) when `phase.sequential` is true. Accumulates findings,
|
|
42
|
+
* checks early stopping between phases, then runs synthesis on all collected
|
|
43
|
+
* findings.
|
|
44
|
+
*
|
|
45
|
+
* @param subject - The subject to research
|
|
46
|
+
* @param options - Optional abort signal and lifecycle hooks
|
|
47
|
+
* @returns Complete debrief result with findings, synthesis, and cost data
|
|
48
|
+
*/
|
|
49
|
+
debrief(subject: TSubject, options?: {
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
hooks?: LifecycleHooks<TSubject, TOutput>;
|
|
52
|
+
}): Promise<DebriefResult<TOutput>>;
|
|
53
|
+
/**
|
|
54
|
+
* Execute all available sources in a single phase.
|
|
55
|
+
*
|
|
56
|
+
* By default, sources run concurrently via Promise.allSettled(). When
|
|
57
|
+
* `phaseGroup.sequential` is true, sources run one at a time in order,
|
|
58
|
+
* stopping at the first source that returns a non-null finding.
|
|
59
|
+
*
|
|
60
|
+
* Fires onSourceAttempt before each lookup and onSourceComplete after.
|
|
61
|
+
* Returns the phase's findings, attempt/success counts, and cost.
|
|
62
|
+
*/
|
|
63
|
+
private executePhase;
|
|
64
|
+
/**
|
|
65
|
+
* Execute sources concurrently via Promise.allSettled().
|
|
66
|
+
*/
|
|
67
|
+
private executePhaseConcurrently;
|
|
68
|
+
/**
|
|
69
|
+
* Execute sources sequentially, stopping at the first non-null finding.
|
|
70
|
+
* Used for AI model phases where cheapest-first ordering controls cost.
|
|
71
|
+
*/
|
|
72
|
+
private executePhaseSequentially;
|
|
73
|
+
/**
|
|
74
|
+
* Check whether early stopping criteria are met.
|
|
75
|
+
*
|
|
76
|
+
* Returns a reason string if stopping should occur, or null to continue.
|
|
77
|
+
*/
|
|
78
|
+
private checkEarlyStop;
|
|
79
|
+
/**
|
|
80
|
+
* Research multiple subjects with bounded concurrency and lifecycle hooks.
|
|
81
|
+
*
|
|
82
|
+
* Uses ParallelBatchRunner for concurrent subject processing with shared
|
|
83
|
+
* rate limiting. Fires lifecycle hooks at each stage for observability:
|
|
84
|
+
* database writes, progress bars, monitoring dashboards, etc.
|
|
85
|
+
*
|
|
86
|
+
* @param subjects - Array of subjects to research
|
|
87
|
+
* @param hooks - Optional lifecycle hooks for progress and monitoring
|
|
88
|
+
* @returns Map of subject ID → DebriefResult
|
|
89
|
+
*/
|
|
90
|
+
debriefBatch(subjects: TSubject[], hooks?: LifecycleHooks<TSubject, TOutput>): Promise<Map<string | number, DebriefResult<TOutput>>>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAGd,aAAa,EACb,gBAAgB,EAEhB,WAAW,EACX,cAAc,EAEf,MAAM,YAAY,CAAA;AAanB;;;;;;GAMG;AACH,qBAAa,oBAAoB,CAAC,QAAQ,SAAS,eAAe,EAAE,OAAO;IACzE,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,SAAS,CAAmB;gBAGlC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EACpC,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,EAC3C,MAAM,GAAE,cAAmB;IAwB7B;;;;;;;;;;;;OAYG;IACG,OAAO,CACX,OAAO,EAAE,QAAQ,EACjB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;KAAE,GAC5E,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAsElC;;;;;;;;;OASG;YACW,YAAY;IA6B1B;;OAEG;YACW,wBAAwB;IAiEtC;;;OAGG;YACW,wBAAwB;IAmEtC;;;;OAIG;IACH,OAAO,CAAC,cAAc;IA6BtB;;;;;;;;;;OAUG;IACG,YAAY,CAChB,QAAQ,EAAE,QAAQ,EAAE,EACpB,KAAK,CAAC,EAAE,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,GACxC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;CAuGzD"}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research Orchestrator — the core engine that wires sources, phases,
|
|
3
|
+
* rate limiting, cost tracking, and synthesis into a complete pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Two entry points:
|
|
6
|
+
* - `debrief(subject)` — research a single subject through all phases
|
|
7
|
+
* - `debriefBatch(subjects, hooks)` — research multiple subjects with
|
|
8
|
+
* concurrency, cost limits, and lifecycle hooks
|
|
9
|
+
*
|
|
10
|
+
* Algorithm (per subject):
|
|
11
|
+
* 1. For each phase in order:
|
|
12
|
+
* a. Filter to available sources (isAvailable())
|
|
13
|
+
* b. Execute sources — concurrently via Promise.allSettled() by default,
|
|
14
|
+
* or sequentially (stop at first success) when phase.sequential is true
|
|
15
|
+
* c. Collect successful findings, tag with reliability info → ScoredFinding
|
|
16
|
+
* d. Check early-stop: do we have earlyStopThreshold+ high-quality families?
|
|
17
|
+
* e. Check per-subject cost limit
|
|
18
|
+
* 2. Synthesize all accumulated findings via the injected Synthesizer
|
|
19
|
+
* 3. Return DebriefResult
|
|
20
|
+
*/
|
|
21
|
+
import { SourceRateLimiter } from "./rate-limiter.js";
|
|
22
|
+
import { BatchCostTracker } from "./cost-tracker.js";
|
|
23
|
+
import { ParallelBatchRunner } from "./batch-runner.js";
|
|
24
|
+
import { NoopTelemetry } from "./telemetry/noop.js";
|
|
25
|
+
const DEFAULT_CONFIG = {
|
|
26
|
+
concurrency: 5,
|
|
27
|
+
confidenceThreshold: 0.6,
|
|
28
|
+
reliabilityThreshold: 0.6,
|
|
29
|
+
earlyStopThreshold: 3,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Generic research orchestrator that coordinates source phases, rate limiting,
|
|
33
|
+
* cost tracking, and AI synthesis into a complete research pipeline.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam TSubject - The research subject type (extends ResearchSubject)
|
|
36
|
+
* @typeParam TOutput - The structured output type produced by synthesis
|
|
37
|
+
*/
|
|
38
|
+
export class ResearchOrchestrator {
|
|
39
|
+
phases;
|
|
40
|
+
synthesizer;
|
|
41
|
+
config;
|
|
42
|
+
rateLimiter;
|
|
43
|
+
telemetry;
|
|
44
|
+
constructor(phases, synthesizer, config = {}) {
|
|
45
|
+
this.phases = phases;
|
|
46
|
+
this.synthesizer = synthesizer;
|
|
47
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
48
|
+
this.rateLimiter = new SourceRateLimiter();
|
|
49
|
+
this.telemetry = config.telemetry ?? new NoopTelemetry();
|
|
50
|
+
// Inject infrastructure into all sources
|
|
51
|
+
for (const phase of phases) {
|
|
52
|
+
for (const source of phase.sources) {
|
|
53
|
+
if ("setRateLimiter" in source && typeof source.setRateLimiter === "function") {
|
|
54
|
+
source.setRateLimiter(this.rateLimiter);
|
|
55
|
+
}
|
|
56
|
+
if ("setCache" in source && typeof source.setCache === "function" && config.cache) {
|
|
57
|
+
source.setCache(config.cache);
|
|
58
|
+
}
|
|
59
|
+
if ("setTelemetry" in source && typeof source.setTelemetry === "function") {
|
|
60
|
+
source.setTelemetry(this.telemetry);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Research a single subject through all configured phases.
|
|
67
|
+
*
|
|
68
|
+
* Iterates through phases sequentially. Within each phase, sources run
|
|
69
|
+
* concurrently via Promise.allSettled by default, or sequentially (stopping
|
|
70
|
+
* at first success) when `phase.sequential` is true. Accumulates findings,
|
|
71
|
+
* checks early stopping between phases, then runs synthesis on all collected
|
|
72
|
+
* findings.
|
|
73
|
+
*
|
|
74
|
+
* @param subject - The subject to research
|
|
75
|
+
* @param options - Optional abort signal and lifecycle hooks
|
|
76
|
+
* @returns Complete debrief result with findings, synthesis, and cost data
|
|
77
|
+
*/
|
|
78
|
+
async debrief(subject, options) {
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
const allFindings = [];
|
|
81
|
+
let totalCostUsd = 0;
|
|
82
|
+
let sourcesAttempted = 0;
|
|
83
|
+
let sourcesSucceeded = 0;
|
|
84
|
+
let stoppedAtPhase;
|
|
85
|
+
const signal = options?.signal;
|
|
86
|
+
const hooks = options?.hooks;
|
|
87
|
+
for (const phaseGroup of this.phases) {
|
|
88
|
+
if (signal?.aborted)
|
|
89
|
+
break;
|
|
90
|
+
const phaseResult = await this.executePhase(subject, phaseGroup, signal, hooks);
|
|
91
|
+
sourcesAttempted += phaseResult.sourcesAttempted;
|
|
92
|
+
sourcesSucceeded += phaseResult.sourcesSucceeded;
|
|
93
|
+
totalCostUsd += phaseResult.costUsd;
|
|
94
|
+
allFindings.push(...phaseResult.findings);
|
|
95
|
+
hooks?.onPhaseComplete?.(subject, phaseGroup.phase, phaseResult.findings);
|
|
96
|
+
const earlyStopReason = this.checkEarlyStop(allFindings, totalCostUsd);
|
|
97
|
+
if (earlyStopReason) {
|
|
98
|
+
stoppedAtPhase = phaseGroup.phase;
|
|
99
|
+
hooks?.onEarlyStop?.(subject, phaseGroup.phase, earlyStopReason);
|
|
100
|
+
if (earlyStopReason === "cost_limit") {
|
|
101
|
+
hooks?.onCostLimitReached?.(subject, totalCostUsd, this.config.costLimits.maxCostPerSubject);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Synthesize all findings
|
|
107
|
+
let synthesisResult = undefined;
|
|
108
|
+
if (allFindings.length > 0) {
|
|
109
|
+
hooks?.onSynthesisStart?.(subject, allFindings.length);
|
|
110
|
+
try {
|
|
111
|
+
synthesisResult = await this.synthesizer.synthesize(subject, allFindings, this.config.synthesis ?? {});
|
|
112
|
+
totalCostUsd += synthesisResult.costUsd;
|
|
113
|
+
hooks?.onSynthesisComplete?.(subject, synthesisResult);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.telemetry.recordError(error instanceof Error ? error : new Error(String(error)), {
|
|
117
|
+
subject: subject.name,
|
|
118
|
+
phase: "synthesis",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
subject,
|
|
124
|
+
data: synthesisResult?.data ?? null,
|
|
125
|
+
findings: allFindings,
|
|
126
|
+
synthesisResult,
|
|
127
|
+
totalCostUsd,
|
|
128
|
+
sourcesAttempted,
|
|
129
|
+
sourcesSucceeded,
|
|
130
|
+
stoppedAtPhase,
|
|
131
|
+
durationMs: Date.now() - startTime,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Execute all available sources in a single phase.
|
|
136
|
+
*
|
|
137
|
+
* By default, sources run concurrently via Promise.allSettled(). When
|
|
138
|
+
* `phaseGroup.sequential` is true, sources run one at a time in order,
|
|
139
|
+
* stopping at the first source that returns a non-null finding.
|
|
140
|
+
*
|
|
141
|
+
* Fires onSourceAttempt before each lookup and onSourceComplete after.
|
|
142
|
+
* Returns the phase's findings, attempt/success counts, and cost.
|
|
143
|
+
*/
|
|
144
|
+
async executePhase(subject, phaseGroup, signal, hooks) {
|
|
145
|
+
const availableSources = phaseGroup.sources.filter((s) => s.isAvailable());
|
|
146
|
+
if (availableSources.length === 0) {
|
|
147
|
+
return { findings: [], sourcesAttempted: 0, sourcesSucceeded: 0, costUsd: 0 };
|
|
148
|
+
}
|
|
149
|
+
if (phaseGroup.sequential) {
|
|
150
|
+
return this.executePhaseSequentially(subject, availableSources, phaseGroup.phase, signal, hooks);
|
|
151
|
+
}
|
|
152
|
+
return this.executePhaseConcurrently(subject, availableSources, phaseGroup.phase, signal, hooks);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Execute sources concurrently via Promise.allSettled().
|
|
156
|
+
*/
|
|
157
|
+
async executePhaseConcurrently(subject, availableSources, phase, signal, hooks) {
|
|
158
|
+
const timeoutSignal = AbortSignal.timeout(120_000);
|
|
159
|
+
const phaseSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
160
|
+
const results = await Promise.allSettled(availableSources.map(async (source) => {
|
|
161
|
+
hooks?.onSourceAttempt?.(subject, source.name, phase);
|
|
162
|
+
return source.lookup(subject, phaseSignal);
|
|
163
|
+
}));
|
|
164
|
+
const findings = [];
|
|
165
|
+
let costUsd = 0;
|
|
166
|
+
let sourcesSucceeded = 0;
|
|
167
|
+
for (let i = 0; i < results.length; i++) {
|
|
168
|
+
const result = results[i];
|
|
169
|
+
const source = availableSources[i];
|
|
170
|
+
const finding = result.status === "fulfilled" ? result.value : null;
|
|
171
|
+
if (result.status === "rejected") {
|
|
172
|
+
this.telemetry.recordError(result.reason instanceof Error ? result.reason : new Error(String(result.reason)), { source: source.name, subject: subject.name, phase });
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
hooks?.onSourceComplete?.(subject, source.name, finding, finding?.costUsd ?? 0);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
this.telemetry.recordError(error instanceof Error ? error : new Error(String(error)), {
|
|
179
|
+
hook: "onSourceComplete",
|
|
180
|
+
source: source.name,
|
|
181
|
+
subject: subject.name,
|
|
182
|
+
phase,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (finding) {
|
|
186
|
+
costUsd += finding.costUsd;
|
|
187
|
+
sourcesSucceeded++;
|
|
188
|
+
findings.push({
|
|
189
|
+
...finding,
|
|
190
|
+
sourceType: source.type,
|
|
191
|
+
sourceName: source.name,
|
|
192
|
+
reliabilityTier: source.reliabilityTier,
|
|
193
|
+
reliabilityScore: source.reliabilityScore,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { findings, sourcesAttempted: availableSources.length, sourcesSucceeded, costUsd };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Execute sources sequentially, stopping at the first non-null finding.
|
|
201
|
+
* Used for AI model phases where cheapest-first ordering controls cost.
|
|
202
|
+
*/
|
|
203
|
+
async executePhaseSequentially(subject, availableSources, phase, signal, hooks) {
|
|
204
|
+
const timeoutSignal = AbortSignal.timeout(120_000);
|
|
205
|
+
const phaseSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
206
|
+
const findings = [];
|
|
207
|
+
let costUsd = 0;
|
|
208
|
+
let sourcesAttempted = 0;
|
|
209
|
+
let sourcesSucceeded = 0;
|
|
210
|
+
for (const source of availableSources) {
|
|
211
|
+
if (phaseSignal.aborted)
|
|
212
|
+
break;
|
|
213
|
+
sourcesAttempted++;
|
|
214
|
+
let finding = null;
|
|
215
|
+
try {
|
|
216
|
+
hooks?.onSourceAttempt?.(subject, source.name, phase);
|
|
217
|
+
finding = await source.lookup(subject, phaseSignal);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
// Source/hook errors are swallowed — consistent with concurrent path
|
|
221
|
+
// where async callbacks turn throws into rejected promises.
|
|
222
|
+
this.telemetry.recordError(error instanceof Error ? error : new Error(String(error)), {
|
|
223
|
+
source: source.name,
|
|
224
|
+
subject: subject.name,
|
|
225
|
+
phase,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
hooks?.onSourceComplete?.(subject, source.name, finding, finding?.costUsd ?? 0);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
this.telemetry.recordError(error instanceof Error ? error : new Error(String(error)), {
|
|
233
|
+
source: source.name,
|
|
234
|
+
subject: subject.name,
|
|
235
|
+
phase,
|
|
236
|
+
hook: "onSourceComplete",
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (finding) {
|
|
240
|
+
costUsd += finding.costUsd;
|
|
241
|
+
sourcesSucceeded++;
|
|
242
|
+
findings.push({
|
|
243
|
+
...finding,
|
|
244
|
+
sourceType: source.type,
|
|
245
|
+
sourceName: source.name,
|
|
246
|
+
reliabilityTier: source.reliabilityTier,
|
|
247
|
+
reliabilityScore: source.reliabilityScore,
|
|
248
|
+
});
|
|
249
|
+
break; // Stop at first success
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return { findings, sourcesAttempted, sourcesSucceeded, costUsd };
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Check whether early stopping criteria are met.
|
|
256
|
+
*
|
|
257
|
+
* Returns a reason string if stopping should occur, or null to continue.
|
|
258
|
+
*/
|
|
259
|
+
checkEarlyStop(allFindings, totalCostUsd) {
|
|
260
|
+
const confidenceThreshold = this.config.confidenceThreshold ?? DEFAULT_CONFIG.confidenceThreshold;
|
|
261
|
+
const reliabilityThreshold = this.config.reliabilityThreshold ?? DEFAULT_CONFIG.reliabilityThreshold;
|
|
262
|
+
const earlyStopThreshold = this.config.earlyStopThreshold ?? DEFAULT_CONFIG.earlyStopThreshold;
|
|
263
|
+
const highQualityFamilies = new Set(allFindings
|
|
264
|
+
.filter((f) => f.confidence >= confidenceThreshold && f.reliabilityScore >= reliabilityThreshold)
|
|
265
|
+
.map((f) => f.sourceType));
|
|
266
|
+
if (highQualityFamilies.size >= earlyStopThreshold) {
|
|
267
|
+
return `${highQualityFamilies.size} high-quality source families met threshold of ${earlyStopThreshold}`;
|
|
268
|
+
}
|
|
269
|
+
if (this.config.costLimits?.maxCostPerSubject &&
|
|
270
|
+
totalCostUsd >= this.config.costLimits.maxCostPerSubject) {
|
|
271
|
+
return "cost_limit";
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Research multiple subjects with bounded concurrency and lifecycle hooks.
|
|
277
|
+
*
|
|
278
|
+
* Uses ParallelBatchRunner for concurrent subject processing with shared
|
|
279
|
+
* rate limiting. Fires lifecycle hooks at each stage for observability:
|
|
280
|
+
* database writes, progress bars, monitoring dashboards, etc.
|
|
281
|
+
*
|
|
282
|
+
* @param subjects - Array of subjects to research
|
|
283
|
+
* @param hooks - Optional lifecycle hooks for progress and monitoring
|
|
284
|
+
* @returns Map of subject ID → DebriefResult
|
|
285
|
+
*/
|
|
286
|
+
async debriefBatch(subjects, hooks) {
|
|
287
|
+
const startTime = Date.now();
|
|
288
|
+
const concurrency = this.config.concurrency ?? DEFAULT_CONFIG.concurrency;
|
|
289
|
+
const resultMap = new Map();
|
|
290
|
+
const costTracker = this.config.costLimits?.maxTotalCost
|
|
291
|
+
? new BatchCostTracker({
|
|
292
|
+
maxTotalCost: this.config.costLimits.maxTotalCost,
|
|
293
|
+
})
|
|
294
|
+
: undefined;
|
|
295
|
+
hooks?.onRunStart?.(subjects.length, this.config);
|
|
296
|
+
const runner = new ParallelBatchRunner({
|
|
297
|
+
concurrency,
|
|
298
|
+
onItemComplete: (subject, result, progress) => {
|
|
299
|
+
resultMap.set(subject.id, result);
|
|
300
|
+
if (costTracker) {
|
|
301
|
+
costTracker.addSubjectCost(subject.id, result.totalCostUsd);
|
|
302
|
+
}
|
|
303
|
+
hooks?.onSubjectComplete?.(subject, result);
|
|
304
|
+
// Calculate running total cost across all completed subjects
|
|
305
|
+
const runningCost = costTracker?.getTotalCost() ??
|
|
306
|
+
Array.from(resultMap.values()).reduce((sum, r) => sum + r.totalCostUsd, 0);
|
|
307
|
+
hooks?.onBatchProgress?.({
|
|
308
|
+
completed: progress.completed,
|
|
309
|
+
total: progress.total,
|
|
310
|
+
costUsd: runningCost,
|
|
311
|
+
elapsedMs: Date.now() - startTime,
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
onItemError: (subject, error) => {
|
|
315
|
+
// Per-subject errors are not batch failures — the batch continues.
|
|
316
|
+
// Report via onSubjectComplete with null data.
|
|
317
|
+
const errorResult = {
|
|
318
|
+
subject,
|
|
319
|
+
data: null,
|
|
320
|
+
findings: [],
|
|
321
|
+
totalCostUsd: 0,
|
|
322
|
+
sourcesAttempted: 0,
|
|
323
|
+
sourcesSucceeded: 0,
|
|
324
|
+
durationMs: 0,
|
|
325
|
+
};
|
|
326
|
+
resultMap.set(subject.id, errorResult);
|
|
327
|
+
hooks?.onSubjectComplete?.(subject, errorResult);
|
|
328
|
+
this.telemetry.recordError(error, { subject: subject.name, phase: "batch" });
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
const batchResults = await runner.run(subjects, async (subject) => {
|
|
332
|
+
// Check total cost limit before starting
|
|
333
|
+
if (costTracker?.isTotalLimitExceeded()) {
|
|
334
|
+
return {
|
|
335
|
+
subject,
|
|
336
|
+
data: null,
|
|
337
|
+
findings: [],
|
|
338
|
+
totalCostUsd: 0,
|
|
339
|
+
sourcesAttempted: 0,
|
|
340
|
+
sourcesSucceeded: 0,
|
|
341
|
+
durationMs: 0,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
hooks?.onSubjectStart?.(subject, resultMap.size, subjects.length);
|
|
345
|
+
return this.debrief(subject, { hooks });
|
|
346
|
+
});
|
|
347
|
+
// Ensure results from cost-limited subjects (which still succeed via
|
|
348
|
+
// onItemComplete) and any edge cases are captured in the map
|
|
349
|
+
for (const entry of batchResults) {
|
|
350
|
+
if (entry.result && !resultMap.has(entry.item.id)) {
|
|
351
|
+
resultMap.set(entry.item.id, entry.result);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const totalCost = costTracker?.getTotalCost() ??
|
|
355
|
+
Array.from(resultMap.values()).reduce((sum, r) => sum + r.totalCostUsd, 0);
|
|
356
|
+
const succeeded = Array.from(resultMap.values()).filter((r) => r.data !== null).length;
|
|
357
|
+
hooks?.onRunComplete?.({
|
|
358
|
+
completed: resultMap.size,
|
|
359
|
+
total: subjects.length,
|
|
360
|
+
costUsd: totalCost,
|
|
361
|
+
elapsedMs: Date.now() - startTime,
|
|
362
|
+
succeeded,
|
|
363
|
+
failed: resultMap.size - succeeded,
|
|
364
|
+
avgCostPerSubject: resultMap.size > 0 ? totalCost / resultMap.size : 0,
|
|
365
|
+
avgDurationMs: resultMap.size > 0
|
|
366
|
+
? Array.from(resultMap.values()).reduce((sum, r) => sum + r.durationMs, 0) /
|
|
367
|
+
resultMap.size
|
|
368
|
+
: 0,
|
|
369
|
+
});
|
|
370
|
+
return resultMap;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-domain async rate limiter shared across all source instances.
|
|
3
|
+
*
|
|
4
|
+
* Uses an async queue per domain to prevent thundering herd — when multiple
|
|
5
|
+
* callers request the same domain simultaneously, they're serialized so each
|
|
6
|
+
* waits the correct delay after the previous one, rather than all sleeping
|
|
7
|
+
* the same amount and firing together.
|
|
8
|
+
*
|
|
9
|
+
* Example: Three sources all query en.wikipedia.org. Without rate limiting,
|
|
10
|
+
* they'd fire simultaneously and risk being blocked. The rate limiter
|
|
11
|
+
* serializes them with configurable delays between requests.
|
|
12
|
+
*/
|
|
13
|
+
export interface RateLimiterStats {
|
|
14
|
+
totalRequests: number;
|
|
15
|
+
totalWaitMs: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class SourceRateLimiter {
|
|
18
|
+
private domains;
|
|
19
|
+
private getOrCreateDomain;
|
|
20
|
+
/**
|
|
21
|
+
* Acquire permission to make a request to the given domain.
|
|
22
|
+
* Blocks until minDelayMs has passed since the last request to this domain.
|
|
23
|
+
*/
|
|
24
|
+
acquire(domain: string, minDelayMs: number): Promise<void>;
|
|
25
|
+
private processQueue;
|
|
26
|
+
/** Get rate limiting stats per domain */
|
|
27
|
+
getStats(): Map<string, RateLimiterStats>;
|
|
28
|
+
/** Reset all state (useful for testing) */
|
|
29
|
+
reset(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAiC;IAEhD,OAAO,CAAC,iBAAiB;IAezB;;;OAGG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YASlD,YAAY;IAyB1B,yCAAyC;IACzC,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAWzC,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAGd"}
|