pi-smart-router 0.21.0 → 0.22.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/.pi/extensions/smart-router/command-formatters.ts +9 -11
- package/.pi/extensions/smart-router/commands.ts +5 -2
- package/.pi/extensions/smart-router/dataset-export.ts +8 -7
- package/.pi/extensions/smart-router/delegate-stream.ts +8 -5
- package/.pi/extensions/smart-router/delegation-runtime.ts +8 -11
- package/.pi/extensions/smart-router/extension-setup.ts +9 -4
- package/.pi/extensions/smart-router/fleet-bootstrap.ts +19 -21
- package/.pi/extensions/smart-router/index.ts +5 -4
- package/.pi/extensions/smart-router/planning-delegate.ts +9 -8
- package/.pi/extensions/smart-router/pricing-lifecycle.ts +9 -4
- package/.pi/extensions/smart-router/route-and-delegate.ts +13 -12
- package/.pi/extensions/smart-router/routing-context.ts +4 -2
- package/.pi/extensions/smart-router/routing-outcomes.ts +6 -2
- package/.pi/extensions/smart-router/session-lifecycle.ts +5 -2
- package/.pi/extensions/smart-router/stream-delegation.ts +8 -2
- package/.pi/extensions/smart-router/types.ts +13 -16
- package/.pi/extensions/smart-router/utils.ts +5 -3
- package/README.md +28 -2
- package/config/benchmark-profiles.json +2 -2
- package/config/onnx-artifact-pins.json +18 -0
- package/config/operator-config.json.example +1 -0
- package/dist/domain/matching/embedding-provider.d.ts +40 -3
- package/dist/domain/matching/embedding-provider.d.ts.map +1 -1
- package/dist/domain/matching/embedding-provider.js +159 -9
- package/dist/domain/matching/embedding-provider.js.map +1 -1
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/matching/embedding-provider.ts +241 -7
- package/src/index.ts +151 -0
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_HISTORY_LIMIT,
|
|
3
3
|
MAX_HISTORY_LIMIT,
|
|
4
|
-
|
|
4
|
+
aggregateSessionStatsFromFleet,
|
|
5
|
+
DEFAULT_TELEMETRY_CONTRIB_EXPORT_LIMIT,
|
|
6
|
+
parseExportTelemetryContribArgs,
|
|
7
|
+
} from '../../../src/index.js';
|
|
5
8
|
import type {
|
|
6
9
|
ModelProfile,
|
|
7
10
|
PriceCatalog,
|
|
8
11
|
RoutingDecision,
|
|
9
12
|
RoutingTelemetry,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from '../../../src/infrastructure/telemetry/session-stats.js';
|
|
13
|
+
SessionStatsSnapshot,
|
|
14
|
+
PlacementPlanReport,
|
|
15
|
+
} from '../../../src/index.js';
|
|
16
|
+
|
|
15
17
|
import { SMART_ROUTER_USAGE } from './commands.js';
|
|
16
|
-
|
|
17
|
-
DEFAULT_TELEMETRY_CONTRIB_EXPORT_LIMIT,
|
|
18
|
-
parseExportTelemetryContribArgs,
|
|
19
|
-
} from '../../../src/cli/smart-router-cli.js';
|
|
20
|
-
import type { PlacementPlanReport } from '../../../src/infrastructure/hardware/placement-plan.js';
|
|
18
|
+
|
|
21
19
|
import {
|
|
22
20
|
DEFAULT_DATASET_EXPORT_LIMIT,
|
|
23
21
|
MAX_DATASET_EXPORT_LIMIT,
|
|
@@ -10,8 +10,11 @@ import {
|
|
|
10
10
|
parseSmartRouterArgs,
|
|
11
11
|
} from './command-formatters.js';
|
|
12
12
|
import { exportDatasetToFile } from './dataset-export.js';
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
exportTelemetryContrib,
|
|
15
|
+
collectPlacementPlan,
|
|
16
|
+
} from '../../../src/index.js';
|
|
17
|
+
|
|
15
18
|
import { bindSharedModelRegistry, rebuildFleet } from './fleet-bootstrap.js';
|
|
16
19
|
import { refreshPricingCatalog } from './pricing-lifecycle.js';
|
|
17
20
|
import { FLEET_MODE_ENTRY_TYPE } from './session-lifecycle.js';
|
|
@@ -6,15 +6,16 @@ import { createHash } from 'node:crypto';
|
|
|
6
6
|
import {
|
|
7
7
|
attachOutcomeLabelsToExport,
|
|
8
8
|
indexOutcomesByRequestId,
|
|
9
|
-
|
|
10
|
-
import { DATASET_MAX_ENTRIES } from '../../../src/infrastructure/telemetry/dataset-limits.js';
|
|
11
|
-
import {
|
|
9
|
+
DATASET_MAX_ENTRIES,
|
|
12
10
|
DatasetRecorder,
|
|
13
11
|
DATASET_ENABLED_NOTIFY_MESSAGE,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import type {
|
|
17
|
-
|
|
12
|
+
OutcomeRecorder,
|
|
13
|
+
} from '../../../src/index.js';
|
|
14
|
+
import type {
|
|
15
|
+
RoutingDatasetRecord,
|
|
16
|
+
RoutingOutcomeRecord,
|
|
17
|
+
StorePort,
|
|
18
|
+
} from '../../../src/index.js';
|
|
18
19
|
|
|
19
20
|
const DEFAULT_DATASET_EXPORT_DIR = '.pi-smart-router/exports';
|
|
20
21
|
const DEFAULT_DATASET_EXPORT_LIMIT = DATASET_MAX_ENTRIES;
|
|
@@ -9,13 +9,16 @@ import {
|
|
|
9
9
|
streamSimple as compatDelegateStream,
|
|
10
10
|
} from '@earendil-works/pi-ai/compat';
|
|
11
11
|
|
|
12
|
-
import { parseAssistantMessageError } from '../../../src/infrastructure/delegation/provider-error.js';
|
|
13
|
-
import { extractUsageActuals } from '../../../src/infrastructure/telemetry/routing-telemetry.js';
|
|
14
12
|
import {
|
|
13
|
+
parseAssistantMessageError,
|
|
14
|
+
extractUsageActuals,
|
|
15
15
|
resolveAdaptiveReasoning,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
} from '../../../src/index.js';
|
|
17
|
+
import type {
|
|
18
|
+
AdaptiveReasoningResult,
|
|
19
|
+
AdaptiveReasoningSignal,
|
|
20
|
+
} from '../../../src/index.js';
|
|
21
|
+
|
|
19
22
|
import {
|
|
20
23
|
buildDelegationContext,
|
|
21
24
|
forwardDelegatedEvent,
|
|
@@ -15,26 +15,23 @@ import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
applyConcisenessHint,
|
|
18
|
-
type AdaptiveReasoningResult,
|
|
19
|
-
} from '../../../src/domain/delegation/adaptive-reasoning.js';
|
|
20
|
-
import {
|
|
21
18
|
isGoogleDelegationTarget,
|
|
22
19
|
normalizeDelegationContext,
|
|
23
20
|
repairGeminiReplayContext,
|
|
24
|
-
} from '../../../src/domain/delegation/delegation-context.js';
|
|
25
|
-
import {
|
|
26
21
|
computeOutputHeadroom,
|
|
27
|
-
type OutputHeadroomConfig,
|
|
28
|
-
} from '../../../src/domain/delegation/output-headroom.js';
|
|
29
|
-
import type { ModelProfile } from '../../../src/domain/types/index.js';
|
|
30
|
-
import {
|
|
31
22
|
formatGeminiThoughtSignatureErrorMessage,
|
|
32
23
|
formatProviderErrorMessage,
|
|
33
24
|
isGeminiThoughtSignatureAssistantError,
|
|
34
25
|
parseAssistantMessageError,
|
|
35
26
|
sanitizeLengthStopMessage,
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
} from '../../../src/index.js';
|
|
28
|
+
import type {
|
|
29
|
+
AdaptiveReasoningResult,
|
|
30
|
+
OutputHeadroomConfig,
|
|
31
|
+
ModelProfile,
|
|
32
|
+
LengthStopHints,
|
|
33
|
+
} from '../../../src/index.js';
|
|
34
|
+
|
|
38
35
|
import type { StreamDelegationDeps } from './types.js';
|
|
39
36
|
|
|
40
37
|
/** Stream options safe to forward to a delegated provider call. */
|
|
@@ -4,10 +4,15 @@ import {
|
|
|
4
4
|
type ExtensionAPI,
|
|
5
5
|
} from '@earendil-works/pi-coding-agent';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import {
|
|
8
|
+
resolveOperatorConfigFromEnv,
|
|
9
|
+
ExecutionLedger,
|
|
10
|
+
createRouterFromFleet,
|
|
11
|
+
LifecycleHookState,
|
|
12
|
+
} from '../../../src/index.js';
|
|
13
|
+
import type {
|
|
14
|
+
SessionRoutingSnapshot,
|
|
15
|
+
} from '../../../src/index.js';
|
|
11
16
|
|
|
12
17
|
import { registerSmartRouterCommand } from './commands.js';
|
|
13
18
|
import {
|
|
@@ -2,39 +2,37 @@ import type { Api, Model } from '@earendil-works/pi-ai/compat';
|
|
|
2
2
|
import {
|
|
3
3
|
ModelRegistry,
|
|
4
4
|
SettingsManager,
|
|
5
|
-
|
|
5
|
+
ExtensionAPI,
|
|
6
6
|
} from '@earendil-works/pi-coding-agent';
|
|
7
7
|
|
|
8
|
-
import { mapFleetFromRegistry } from '../../../src/config/pi-model-mapper.js';
|
|
9
8
|
import {
|
|
9
|
+
mapFleetFromRegistry,
|
|
10
10
|
DEFAULT_OPERATOR_CONFIG,
|
|
11
11
|
resolveOperatorConfigFromEnv,
|
|
12
|
-
} from '../../../src/config/defaults.js';
|
|
13
|
-
import {
|
|
14
12
|
HydraMatcher,
|
|
15
13
|
createOnnxEmbeddingProvider,
|
|
16
|
-
|
|
17
|
-
import { SessionPinner } from '../../../src/domain/pinning/session-pinner.js';
|
|
18
|
-
import {
|
|
14
|
+
SessionPinner,
|
|
19
15
|
collectPoolModelIds,
|
|
20
16
|
resolveQuotaWindowEstimateConfigFromEnv,
|
|
21
17
|
resolveQuotaWindowPosition,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import type { QuotaWindowPosition } from '../../../src/domain/types/entities.js';
|
|
27
|
-
import type { OperatorConfig } from '../../../src/domain/types/schemas.js';
|
|
28
|
-
import type { StorePort } from '../../../src/domain/types/store-port.js';
|
|
29
|
-
import { getDefaultSystemInfo } from '../../../src/infrastructure/hardware/hardware-probe.js';
|
|
30
|
-
import { DEFAULT_LOCAL_CONFIG } from '../../../src/infrastructure/local/local-zero-tier.js';
|
|
31
|
-
import { RoutingTelemetryEmitter } from '../../../src/infrastructure/telemetry/routing-telemetry.js';
|
|
32
|
-
import { applyCatalogPricesToFleet } from '../../../src/infrastructure/pricing/price-broker.js';
|
|
33
|
-
import {
|
|
18
|
+
getDefaultSystemInfo,
|
|
19
|
+
DEFAULT_LOCAL_CONFIG,
|
|
20
|
+
RoutingTelemetryEmitter,
|
|
21
|
+
applyCatalogPricesToFleet,
|
|
34
22
|
createRouterFromFleet,
|
|
35
|
-
type GatewayDispatchOptions,
|
|
36
|
-
type PiExtensionHooks,
|
|
37
23
|
} from '../../../src/index.js';
|
|
24
|
+
import type {
|
|
25
|
+
QuotaWindowAdapter,
|
|
26
|
+
QuotaWindowEstimateConfig,
|
|
27
|
+
ModelProfile,
|
|
28
|
+
PriceCatalog,
|
|
29
|
+
QuotaWindowPosition,
|
|
30
|
+
OperatorConfig,
|
|
31
|
+
StorePort,
|
|
32
|
+
GatewayDispatchOptions,
|
|
33
|
+
PiExtensionHooks,
|
|
34
|
+
} from '../../../src/index.js';
|
|
35
|
+
|
|
38
36
|
import { resolveModelScope } from './pi-model-scope.js';
|
|
39
37
|
import type { FleetMode, SmartRouterRuntime } from './types.js';
|
|
40
38
|
import { resolveRateLimiter } from './utils.js';
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* hooks for routing state. Stream delegation routes each request through the
|
|
7
7
|
* pipeline and forwards to the selected provider's built-in streaming API.
|
|
8
8
|
*
|
|
9
|
-
* Imports
|
|
10
|
-
* from source at dev time and is excluded from
|
|
9
|
+
* Imports the package public facade (../../../src/index.js, not dist/) because
|
|
10
|
+
* the extension is loaded by pi from source at dev time and is excluded from
|
|
11
|
+
* the npm dist artifact. Deep src/** subpath imports are forbidden (SP-256/257).
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
@@ -116,14 +117,14 @@ export { routeAndDelegate } from './route-and-delegate.js';
|
|
|
116
117
|
export {
|
|
117
118
|
formatGeminiThoughtSignatureErrorMessage,
|
|
118
119
|
isGeminiThoughtSignatureAssistantError,
|
|
119
|
-
} from '../../../src/
|
|
120
|
+
} from '../../../src/index.js';
|
|
120
121
|
export {
|
|
121
122
|
GEMINI_TOOL_HISTORY_EXCLUDED,
|
|
122
123
|
hasToolCallHistory,
|
|
123
124
|
hasToolCallHistoryFromContext,
|
|
124
125
|
isGoogleGeminiProfile,
|
|
125
126
|
resolveEffectiveFleet,
|
|
126
|
-
} from '../../../src/
|
|
127
|
+
} from '../../../src/index.js';
|
|
127
128
|
|
|
128
129
|
export default async function smartRouterExtension(pi: ExtensionAPI): Promise<void> {
|
|
129
130
|
const cwd = process.cwd();
|
|
@@ -22,21 +22,22 @@ import {
|
|
|
22
22
|
type TextContent,
|
|
23
23
|
} from '@earendil-works/pi-ai/compat';
|
|
24
24
|
|
|
25
|
-
import type {
|
|
26
|
-
CompressedContextSpec,
|
|
27
|
-
PlanningDelegateObservability,
|
|
28
|
-
RoutingDecision,
|
|
29
|
-
} from '../../../src/domain/types/index.js';
|
|
30
|
-
import { resolveAdaptiveReasoning } from '../../../src/domain/delegation/adaptive-reasoning.js';
|
|
31
|
-
import { DEFAULT_PLANNING_DELEGATE_CONFIG } from '../../../src/domain/types/schemas.js';
|
|
32
25
|
import {
|
|
26
|
+
resolveAdaptiveReasoning,
|
|
27
|
+
DEFAULT_PLANNING_DELEGATE_CONFIG,
|
|
33
28
|
createPlanningDelegateObservability,
|
|
34
29
|
enrichRoutingDecisionWithPlanningDelegate,
|
|
35
30
|
PLANNING_DELEGATE,
|
|
36
31
|
PLANNING_DELEGATE_TIMEOUT,
|
|
37
32
|
PLANNING_DELEGATE_UNAVAILABLE,
|
|
38
33
|
PLANNING_DIRECT_FRONTIER,
|
|
39
|
-
} from '../../../src/
|
|
34
|
+
} from '../../../src/index.js';
|
|
35
|
+
import type {
|
|
36
|
+
CompressedContextSpec,
|
|
37
|
+
PlanningDelegateObservability,
|
|
38
|
+
RoutingDecision,
|
|
39
|
+
} from '../../../src/index.js';
|
|
40
|
+
|
|
40
41
|
import { collectDelegatedStream } from './delegate-stream.js';
|
|
41
42
|
import { findFleetProfile, resolveRegistryModel } from './delegation-runtime.js';
|
|
42
43
|
import type { StreamDelegationDeps } from './types.js';
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_OPERATOR_CONFIG,
|
|
3
|
+
fetchLitellmPriceCatalog,
|
|
4
|
+
checkStaleness,
|
|
5
|
+
} from '../../../src/index.js';
|
|
6
|
+
import type {
|
|
7
|
+
PriceCatalog,
|
|
8
|
+
} from '../../../src/index.js';
|
|
9
|
+
|
|
5
10
|
import type { SmartRouterRuntime } from './types.js';
|
|
6
11
|
|
|
7
12
|
export async function refreshPricingCatalog(
|
|
@@ -7,27 +7,28 @@ import type {
|
|
|
7
7
|
SimpleStreamOptions,
|
|
8
8
|
} from '@earendil-works/pi-ai/compat';
|
|
9
9
|
|
|
10
|
-
import { safeCloudDefault } from '../../../src/domain/pipeline/safe-default.js';
|
|
11
|
-
import { computeOutputHeadroom } from '../../../src/domain/delegation/output-headroom.js';
|
|
12
|
-
import { resolvePeakPricingAdjustment } from '../../../src/domain/pricing/peak-pricing.js';
|
|
13
10
|
import {
|
|
11
|
+
safeCloudDefault,
|
|
12
|
+
computeOutputHeadroom,
|
|
13
|
+
resolvePeakPricingAdjustment,
|
|
14
14
|
CONTEXT_OVERFLOW_NO_FIT,
|
|
15
15
|
resolveContextOverflowFallback,
|
|
16
|
-
} from '../../../src/domain/routing/context-fit.js';
|
|
17
|
-
import {
|
|
18
16
|
assertRoutableFleetAfterGeminiToolHistoryGuard,
|
|
19
17
|
GEMINI_TOOL_HISTORY_EXCLUDED,
|
|
20
18
|
isGoogleGeminiProfile,
|
|
21
19
|
resolveEffectiveFleet,
|
|
22
|
-
} from '../../../src/domain/routing/tool-history-guard.js';
|
|
23
|
-
import type { ModelProfile, RoutingDecision, RoutingRequest } from '../../../src/domain/types/index.js';
|
|
24
|
-
import type { GeminiToolHistoryGuardResult } from '../../../src/domain/routing/tool-history-guard.js';
|
|
25
|
-
import {
|
|
26
20
|
isGeminiThoughtSignatureAssistantError,
|
|
27
21
|
parseAssistantMessageError,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
GEMINI_REPLAY_INCOMPATIBLE,
|
|
23
|
+
shouldFailoverOnProviderError,
|
|
24
|
+
} from '../../../src/index.js';
|
|
25
|
+
import type {
|
|
26
|
+
ModelProfile,
|
|
27
|
+
RoutingDecision,
|
|
28
|
+
RoutingRequest,
|
|
29
|
+
GeminiToolHistoryGuardResult,
|
|
30
|
+
} from '../../../src/index.js';
|
|
31
|
+
|
|
31
32
|
import {
|
|
32
33
|
commitPipedTerminal,
|
|
33
34
|
delegateWithOutcome,
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
type TextContent,
|
|
8
8
|
} from '@earendil-works/pi-ai/compat';
|
|
9
9
|
|
|
10
|
+
import {
|
|
11
|
+
LifecycleHookState,
|
|
12
|
+
} from '../../../src/index.js';
|
|
10
13
|
import type {
|
|
11
14
|
Message as RoutingMessage,
|
|
12
15
|
RoutingRequest,
|
|
13
16
|
TurnType,
|
|
14
|
-
} from '../../../src/
|
|
15
|
-
import { LifecycleHookState } from '../../../src/index.js';
|
|
17
|
+
} from '../../../src/index.js';
|
|
16
18
|
|
|
17
19
|
const TOKEN_ESTIMATE_KEYS = [
|
|
18
20
|
'estimatedInputTokens',
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
RoutingDecision,
|
|
3
|
+
RoutingRequest,
|
|
4
|
+
SessionRoutingSnapshot,
|
|
5
|
+
} from '../../../src/index.js';
|
|
6
|
+
|
|
3
7
|
import type { StreamDelegationDeps } from './types.js';
|
|
4
8
|
|
|
5
9
|
export function capturePreRouteOutcomes(
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
evictInMemorySessionState,
|
|
5
|
+
SessionPinner,
|
|
6
|
+
} from '../../../src/index.js';
|
|
7
|
+
|
|
5
8
|
import {
|
|
6
9
|
bindSharedModelRegistry,
|
|
7
10
|
ensureFleetFresh,
|
|
@@ -7,8 +7,14 @@ import {
|
|
|
7
7
|
createAssistantMessageEventStream,
|
|
8
8
|
} from '@earendil-works/pi-ai/compat';
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
resolvePeakPricingAdjustment,
|
|
12
|
+
} from '../../../src/index.js';
|
|
13
|
+
import type {
|
|
14
|
+
RoutingDecision,
|
|
15
|
+
RoutingFeatureSidecar,
|
|
16
|
+
} from '../../../src/index.js';
|
|
17
|
+
|
|
12
18
|
import { createErrorMessage } from './delegation-runtime.js';
|
|
13
19
|
import { routeAndDelegate } from './route-and-delegate.js';
|
|
14
20
|
import type { StreamDelegationDeps } from './types.js';
|
|
@@ -7,10 +7,15 @@ import type {
|
|
|
7
7
|
} from '@earendil-works/pi-ai/compat';
|
|
8
8
|
import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
ExecutionLedger,
|
|
12
|
+
SessionPinner,
|
|
13
|
+
DatasetRecorder,
|
|
14
|
+
OutcomeRecorder,
|
|
15
|
+
LifecycleHookState,
|
|
16
|
+
} from '../../../src/index.js';
|
|
13
17
|
import type {
|
|
18
|
+
HydraMatcher,
|
|
14
19
|
AdaptiveReasoningConfig,
|
|
15
20
|
ModelProfile,
|
|
16
21
|
PlanningDelegateConfig,
|
|
@@ -18,21 +23,13 @@ import type {
|
|
|
18
23
|
RoutingDecision,
|
|
19
24
|
RoutingReasoningTelemetry,
|
|
20
25
|
RoutingUsageActuals,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
DatasetRecorder,
|
|
26
|
-
} from '../../../src/infrastructure/telemetry/dataset-recorder.js';
|
|
27
|
-
import {
|
|
28
|
-
OutcomeRecorder,
|
|
29
|
-
type SessionRoutingSnapshot,
|
|
30
|
-
} from '../../../src/infrastructure/telemetry/outcome-recorder.js';
|
|
31
|
-
import {
|
|
32
|
-
LifecycleHookState,
|
|
33
|
-
type RouterHandle,
|
|
26
|
+
StorePort,
|
|
27
|
+
SessionRoutingSnapshot,
|
|
28
|
+
RouterHandle,
|
|
34
29
|
} from '../../../src/index.js';
|
|
35
30
|
|
|
31
|
+
import type { PlanningDelegateSpawnFn } from './planning-delegate.js';
|
|
32
|
+
|
|
36
33
|
export type FleetMode = 'scoped' | 'all';
|
|
37
34
|
|
|
38
35
|
export type SmartRouterCommand =
|
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
createResilientStore,
|
|
5
5
|
SqliteStore,
|
|
6
6
|
SqliteStoreError,
|
|
7
|
-
} from '../../../src/
|
|
8
|
-
import type {
|
|
9
|
-
|
|
7
|
+
} from '../../../src/index.js';
|
|
8
|
+
import type {
|
|
9
|
+
StorePort,
|
|
10
|
+
RateLimitPort,
|
|
11
|
+
} from '../../../src/index.js';
|
|
10
12
|
|
|
11
13
|
export const DEFAULT_ROUTER_STATE_DB_PATH = '.pi-smart-router/state.db';
|
|
12
14
|
export const DEFAULT_RATE_LIMIT_MAX_TOKENS = 60;
|
package/README.md
CHANGED
|
@@ -290,7 +290,7 @@ These behaviors run in `.pi/extensions/smart-router/` and have **no equivalent i
|
|
|
290
290
|
### Recommended integration path
|
|
291
291
|
|
|
292
292
|
- **pi users:** install the **extension** (`pi install npm:pi-smart-router`). It is the full product — everything in the table above works out of the box, including failover, delegate spawn, headroom escalation, and quota reaction.
|
|
293
|
-
- **npm embedders:** you get the **routing core** (12-stage pipeline, fleet mapping, telemetry, gateway health tracking, failover *selection*). Plan to implement your own stream delegation, failover iteration, headroom checks, and planning-delegate spawn around the decisions the pipeline returns — or track [#149](https://github.com/beettlle/pi-smart-router/issues/149) (**extension public facade**), the migration plan for exposing the extension's stream/delegation surface as supported library API so this gap closes over time. Until #149 lands, the extension modules also import `src/**` internals directly, so deep imports into `src/` are not a stable API.
|
|
293
|
+
- **npm embedders:** you get the **routing core** (12-stage pipeline, fleet mapping, telemetry, gateway health tracking, failover *selection*). Plan to implement your own stream delegation, failover iteration, headroom checks, and planning-delegate spawn around the decisions the pipeline returns — or track [#149](https://github.com/beettlle/pi-smart-router/issues/149) (**extension public facade**), the migration plan for exposing the extension's stream/delegation surface as supported library API so this gap closes over time. Until #149 lands, the extension modules also import `src/**` internals directly, so deep imports into `src/` are not a stable API. See [docs/extension-package-boundary.md](docs/extension-package-boundary.md) for the facade vs internal-API boundary, the deep-import lint guard, and the extension coverage gate.
|
|
294
294
|
|
|
295
295
|
```text
|
|
296
296
|
pi extension path (full product) npm library path (routing core)
|
|
@@ -972,6 +972,32 @@ npm run benchmark:encoder
|
|
|
972
972
|
|
|
973
973
|
The script reports p50/p95 latency for each encoder and asserts Granite p50/p95 stay within the 120 ms budget ceiling. Requires `@huggingface/transformers` and a one-time ONNX artifact download.
|
|
974
974
|
|
|
975
|
+
#### Supply-chain: artifact pins, offline cache, and audit posture
|
|
976
|
+
|
|
977
|
+
**Digest pinning (SP-259, [#147](https://github.com/beettlle/pi-smart-router/issues/147)).** The embedder verifies cached ONNX artifacts against SHA-256 pins before they are used. Pins live in [`config/onnx-artifact-pins.json`](config/onnx-artifact-pins.json) (`pins[modelId][cacheRelativePath] = sha256`; digests are the HuggingFace LFS oids for the default quantized artifacts). Pin mode is controlled by `SMART_ROUTER_ONNX_PIN_MODE`:
|
|
978
|
+
|
|
979
|
+
| Mode | Behavior |
|
|
980
|
+
|------|----------|
|
|
981
|
+
| `off` (default) | No verification — first run downloads unpinned (local dogfood preserved). |
|
|
982
|
+
| `verify` | Configured pins are verified after load; models without pins still download unpinned. |
|
|
983
|
+
| `enforce` | CI/prod: pins are **required** for the loaded model — missing pin file, missing pins for the model, missing cached artifact, or any digest mismatch all fail closed. |
|
|
984
|
+
|
|
985
|
+
Override the pin file location with `SMART_ROUTER_ONNX_PIN_FILE`. Verification runs **after** the transformers.js pipeline loads but **before** the embedder is returned, so even a first-run anonymous download is checked against configured pins — tampered or unexpected bytes fail closed and the session is never usable. Anonymous fetch does not silently bypass pins when they are configured. When upgrading model revisions, re-fetch digests from the HuggingFace API out-of-band and update the pin file; never pin a digest you have not verified.
|
|
986
|
+
|
|
987
|
+
**Offline / air-gapped cache warm.** The artifact cache (`hydra.artifact_cache_path`, default `.pi-smart-router/models/`) is fully self-contained once warmed:
|
|
988
|
+
|
|
989
|
+
1. On a networked host, warm the cache with the encoder you deploy — run any routed request, or `npm run benchmark:encoder -- --cache .pi-smart-router/models/`.
|
|
990
|
+
2. Enable pin mode (`verify` or `enforce`) during the warm so digest mismatches surface on the networked host, before rollout.
|
|
991
|
+
3. Copy the cache directory (and `config/onnx-artifact-pins.json` when pinning) to the offline host and point `hydra.artifact_cache_path` at the copy.
|
|
992
|
+
4. Defense in depth: embedders can set `env.allowRemoteModels = false` (from `@huggingface/transformers`) before routing to forbid any network fetch, so no anonymous download path exists in production.
|
|
993
|
+
|
|
994
|
+
**`npm audit` posture for the transformers chain.** The audit baseline includes high-severity advisories in `@huggingface/transformers` → `onnxruntime-node` → `adm-zip` ([GHSA-xcpc-8h2w-3j85](https://github.com/advisories/GHSA-xcpc-8h2w-3j85) — crafted ZIP triggers a 4GB memory allocation) and `sharp` (libvips CVEs), both **with no upstream fix available**. Accepted-risk rationale:
|
|
995
|
+
|
|
996
|
+
- Model bytes in the pinned production path come only from the operator-controlled local cache, verified by SHA-256 — the vulnerable archive-decompression path is not exposed to untrusted input there.
|
|
997
|
+
- Inference runs locally with no gradients and no untrusted deserialization; untrusted bytes enter only on first cache warm, over TLS from the HuggingFace hub, and (with pin mode on) are digest-checked before use.
|
|
998
|
+
|
|
999
|
+
**Monitoring policy:** run `npm audit` at each release and record the baseline. A *new* high-severity advisory in the transformers/onnxruntime chain requires a documented exception with rationale (release notes or a linked issue) — never silently dismiss; exceptions without rationale are release blockers.
|
|
1000
|
+
|
|
975
1001
|
## Architecture
|
|
976
1002
|
|
|
977
1003
|
### Three execution tiers
|
|
@@ -1102,7 +1128,7 @@ Contributors must run `npm run build` before publishing or consuming the library
|
|
|
1102
1128
|
| `npm run verify:ci` | Full CI parity: build, typecheck, lint, test, coverage (baseline PR gate; see [PR and pre-release quality gate set](#pr-and-pre-release-quality-gate-set)) |
|
|
1103
1129
|
| `npm run typecheck` | TypeScript strict mode check (`tsc --noEmit`) |
|
|
1104
1130
|
| `npm test` | Run test suite (`vitest run`) |
|
|
1105
|
-
| `npm run coverage:check` | Tests with line-coverage thresholds |
|
|
1131
|
+
| `npm run coverage:check` | Tests with line-coverage thresholds (src + extension, 80% floor — see [docs/extension-package-boundary.md](docs/extension-package-boundary.md#extension-coverage-gate-144)) |
|
|
1106
1132
|
| `npm run lint` | ESLint + fleet catalog validation |
|
|
1107
1133
|
| `npm run routing:bootstrap-centroids` | Regenerate `config/routing-centroids.json` from cluster catalog |
|
|
1108
1134
|
| `npm run routing:calibration-aggregate` | Aggregate community telemetry for calibration |
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"livecodebench": "https://livecodebench.github.io/leaderboard.html",
|
|
8
8
|
"bfcl": "https://gorilla.cs.berkeley.edu/leaderboard.html"
|
|
9
9
|
},
|
|
10
|
-
"scrape_date": "2026-09-
|
|
11
|
-
"catalog_freeze_date": "2026-09-
|
|
10
|
+
"scrape_date": "2026-09-05",
|
|
11
|
+
"catalog_freeze_date": "2026-09-05"
|
|
12
12
|
},
|
|
13
13
|
"aliases": {
|
|
14
14
|
"anthropic/claude-opus-4": "claude-opus-4-5",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_documentation": {
|
|
3
|
+
"purpose": "SP-259 (#147) — SHA-256 digest pins for ONNX embedding artifacts loaded by src/domain/matching/embedding-provider.ts. Verified on load when pin mode is enabled; mismatch or missing pinned artifact fails closed.",
|
|
4
|
+
"activation": "Set SMART_ROUTER_ONNX_PIN_MODE=verify (verify configured pins; unpinned models still download) or =enforce (CI/prod — pins REQUIRED for the loaded model; missing pin file, missing pins, missing cached artifact, or any mismatch fails closed). Default is off (unpinned local dogfood preserved). Override this file path with SMART_ROUTER_ONNX_PIN_FILE.",
|
|
5
|
+
"format": "pins[modelId][cacheRelativePath] = lowercase hex SHA-256. Paths are relative to the model directory inside the transformers.js cache (hydra.artifact_cache_path): <cache>/<modelId>/<path> (hub-style models--org--name/snapshots/<rev>/ layout also supported).",
|
|
6
|
+
"digest_source": "Digests are HuggingFace LFS oids (= SHA-256 of file content) from https://huggingface.co/api/models/<modelId>/tree/main/onnx, fetched 2026-09-05. onnx/model_quantized.onnx is the default q8 artifact transformers.js loads for feature-extraction.",
|
|
7
|
+
"refresh": "Re-fetch digests from the HF API when upgrading model revisions; never pin digests you have not verified out-of-band."
|
|
8
|
+
},
|
|
9
|
+
"version": 1,
|
|
10
|
+
"pins": {
|
|
11
|
+
"Xenova/all-MiniLM-L6-v2": {
|
|
12
|
+
"onnx/model_quantized.onnx": "afdb6f1a0e45b715d0bb9b11772f032c399babd23bfc31fed1c170afc848bdb1"
|
|
13
|
+
},
|
|
14
|
+
"onnx-community/granite-embedding-97m-multilingual-r2-ONNX": {
|
|
15
|
+
"onnx/model_quantized.onnx": "704c1ebca5fbb7cd83ced41827658ac4c9990c64f7f2874d22b78044e5022e22"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"artifact_cache_path": ".pi-smart-router/models/",
|
|
23
23
|
"encoder": "minilm",
|
|
24
24
|
"hydra_heads": "learned_projection",
|
|
25
|
+
"_onnx_artifact_pins_documentation": "SP-259 (#147) — ONNX artifact supply-chain pinning. Digests live in config/onnx-artifact-pins.json (SHA-256 per cached artifact). Enable with env SMART_ROUTER_ONNX_PIN_MODE=verify (verify configured pins; unpinned models still download) or =enforce (CI/prod: pins required for the loaded model — missing pin file, missing pins, missing cached artifact, or any digest mismatch fails closed). Default off preserves first-run unpinned dogfood downloads. Override pin file path with SMART_ROUTER_ONNX_PIN_FILE. See README 'HyDRA model cache → Supply-chain' for offline cache warm and audit posture.",
|
|
25
26
|
"_hydra_heads_documentation": {
|
|
26
27
|
"learned_projection": "Default — SP-115 384×3 linear projection on MiniLM/Granite embeddings (config/hydra-projection-weights.json). Approximates HyDRA K sigmoid heads; keep until calibration Top-1 error exceeds ~10%.",
|
|
27
28
|
"modernbert_k4": "ModernBERT-base [CLS] with K=4 independent sigmoid heads (config/modernbert-k4-heads.json). Migrating from SP-115: train K=4 head weights offline, set hydra_heads to modernbert_k4, and remove or ignore hydra-projection-weights.json. The fourth debugging dimension is predicted for HyDRA fidelity but excluded from the catalog-decoupled shortfall gate (reasoning, code_gen, tool_use only). See docs/routing-roadmap.md §2 P3 and GitHub #81."
|
|
@@ -18,6 +18,43 @@ export interface TextEmbedder {
|
|
|
18
18
|
embed(text: string): Promise<Float32Array>;
|
|
19
19
|
dispose(): Promise<void>;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Artifact pin verification mode:
|
|
23
|
+
* - `off` — no verification (default; preserves unpinned local dogfood downloads)
|
|
24
|
+
* - `verify` — verify cached artifacts when pins exist for the model; fail
|
|
25
|
+
* closed on digest mismatch or missing cached artifact. Models without pins
|
|
26
|
+
* remain unpinned (first-run download allowed).
|
|
27
|
+
* - `enforce` — CI/prod mode: pins are REQUIRED for the model; fail closed when
|
|
28
|
+
* the pin file is missing/unparseable, the model has no pins, a pinned
|
|
29
|
+
* artifact is missing from the cache, or any digest mismatches.
|
|
30
|
+
*/
|
|
31
|
+
export type OnnxArtifactPinMode = 'off' | 'verify' | 'enforce';
|
|
32
|
+
export declare const ONNX_ARTIFACT_PIN_MODES: readonly OnnxArtifactPinMode[];
|
|
33
|
+
/** Default pin file (relative to process CWD). Override via options or env. */
|
|
34
|
+
export declare const DEFAULT_ONNX_PIN_FILE = "config/onnx-artifact-pins.json";
|
|
35
|
+
/** Env vars (operator wiring; schemas/hydra-matcher out of SP-259 scope). */
|
|
36
|
+
export declare const ONNX_PIN_MODE_ENV = "SMART_ROUTER_ONNX_PIN_MODE";
|
|
37
|
+
export declare const ONNX_PIN_FILE_ENV = "SMART_ROUTER_ONNX_PIN_FILE";
|
|
38
|
+
/** Pin file shape: per-model map of cache-relative file path → SHA-256 hex. */
|
|
39
|
+
export interface OnnxArtifactPinFile {
|
|
40
|
+
readonly version: number;
|
|
41
|
+
readonly pins: Record<string, Record<string, string>>;
|
|
42
|
+
}
|
|
43
|
+
export interface OnnxPinOptions {
|
|
44
|
+
/** Pin mode. Default: $SMART_ROUTER_ONNX_PIN_MODE, else 'off'. */
|
|
45
|
+
readonly pinMode?: OnnxArtifactPinMode;
|
|
46
|
+
/** Pin file path. Default: $SMART_ROUTER_ONNX_PIN_FILE, else DEFAULT_ONNX_PIN_FILE. */
|
|
47
|
+
readonly pinFilePath?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify cached ONNX artifacts for `modelId` against configured SHA-256 pins.
|
|
51
|
+
* Fails closed on digest mismatch or missing pinned artifact; in `enforce`
|
|
52
|
+
* mode also fails when the pin file or model pins are missing.
|
|
53
|
+
*
|
|
54
|
+
* Runs after pipeline load so first-run downloads are verified before the
|
|
55
|
+
* embedder is returned; a mismatching session is never usable.
|
|
56
|
+
*/
|
|
57
|
+
export declare function verifyOnnxArtifactPins(modelId: string, cachePath: string, options?: OnnxPinOptions): Promise<void>;
|
|
21
58
|
/**
|
|
22
59
|
* Creates a TextEmbedder backed by @huggingface/transformers ONNX runtime.
|
|
23
60
|
* Model: Xenova/all-MiniLM-L6-v2 (384-dim).
|
|
@@ -25,12 +62,12 @@ export interface TextEmbedder {
|
|
|
25
62
|
* The package is loaded dynamically — not required at compile time.
|
|
26
63
|
* Install: `npm i @huggingface/transformers`
|
|
27
64
|
*/
|
|
28
|
-
export declare function createOnnxTextEmbedder(artifactCachePath: string): Promise<TextEmbedder>;
|
|
65
|
+
export declare function createOnnxTextEmbedder(artifactCachePath: string, pinOptions?: OnnxPinOptions): Promise<TextEmbedder>;
|
|
29
66
|
/**
|
|
30
67
|
* Granite 97M long-context embedder (384-dim ONNX drop-in for SP-115 head).
|
|
31
68
|
* Model: ibm-granite/granite-embedding-97m-multilingual-r2 via ONNX runtime.
|
|
32
69
|
*/
|
|
33
|
-
export declare function createGraniteOnnxTextEmbedder(artifactCachePath: string): Promise<TextEmbedder>;
|
|
70
|
+
export declare function createGraniteOnnxTextEmbedder(artifactCachePath: string, pinOptions?: OnnxPinOptions): Promise<TextEmbedder>;
|
|
34
71
|
/** Select ONNX text embedder by operator encoder flag. */
|
|
35
|
-
export declare function createTextEmbedder(encoder: Encoder | undefined, artifactCachePath: string): Promise<TextEmbedder>;
|
|
72
|
+
export declare function createTextEmbedder(encoder: Encoder | undefined, artifactCachePath: string, pinOptions?: OnnxPinOptions): Promise<TextEmbedder>;
|
|
36
73
|
//# sourceMappingURL=embedding-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding-provider.d.ts","sourceRoot":"","sources":["../../../src/domain/matching/embedding-provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"embedding-provider.d.ts","sourceRoot":"","sources":["../../../src/domain/matching/embedding-provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,kBAAkB,8DAC8B,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAID;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/D,eAAO,MAAM,uBAAuB,EAAE,SAAS,mBAAmB,EAIjE,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,mCAAmC,CAAC;AAEtE,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,uFAAuF;IACvF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AA6FD;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAuCf;AA0GD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,iBAAiB,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,cAAc,GAC1B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,iBAAiB,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,cAAc,GAC1B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,0DAA0D;AAC1D,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,YAAkB,EAClC,iBAAiB,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,cAAc,GAC1B,OAAO,CAAC,YAAY,CAAC,CAWvB"}
|