@juspay/neurolink 12.17.0 → 12.17.2
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/CHANGELOG.md +2 -7
- package/dist/browser/neurolink.min.js +422 -413
- package/dist/cli/commands/proxy.js +455 -259
- package/dist/constants/contextWindows.d.ts +2 -0
- package/dist/constants/contextWindows.js +4 -0
- package/dist/providers/anthropic/client.js +15 -5
- package/dist/proxy/anthropicBudgetResponse.d.ts +3 -0
- package/dist/proxy/anthropicBudgetResponse.js +153 -0
- package/dist/proxy/bodyCaptureProcessing.d.ts +1 -0
- package/dist/proxy/bodyCaptureProcessing.js +23 -4
- package/dist/proxy/bodyCaptureWorker.js +20 -2
- package/dist/proxy/codexFallback.d.ts +10 -0
- package/dist/proxy/codexFallback.js +108 -24
- package/dist/proxy/codexUsage.js +2 -0
- package/dist/proxy/globalInstaller.d.ts +32 -1
- package/dist/proxy/globalInstaller.js +386 -3
- package/dist/proxy/otelLogSink.d.ts +3 -3
- package/dist/proxy/otelLogSink.js +137 -144
- package/dist/proxy/otlpLogTransport.d.ts +3 -0
- package/dist/proxy/otlpLogTransport.js +334 -0
- package/dist/proxy/proxyActivity.d.ts +22 -2
- package/dist/proxy/proxyActivity.js +114 -9
- package/dist/proxy/proxyAnalysis.js +82 -52
- package/dist/proxy/proxyClientCancellation.d.ts +7 -0
- package/dist/proxy/proxyClientCancellation.js +23 -0
- package/dist/proxy/proxyContextPreflight.d.ts +25 -0
- package/dist/proxy/proxyContextPreflight.js +337 -0
- package/dist/proxy/proxyFailureDetails.d.ts +19 -0
- package/dist/proxy/proxyFailureDetails.js +96 -0
- package/dist/proxy/proxyLifecycle.js +7 -0
- package/dist/proxy/proxyRouteBodyCapture.d.ts +13 -0
- package/dist/proxy/proxyRouteBodyCapture.js +112 -0
- package/dist/proxy/proxyTokenBudget.d.ts +40 -0
- package/dist/proxy/proxyTokenBudget.js +523 -0
- package/dist/proxy/proxyTracer.d.ts +3 -2
- package/dist/proxy/proxyTracer.js +46 -20
- package/dist/proxy/proxyTranslationEngine.d.ts +8 -10
- package/dist/proxy/proxyTranslationEngine.js +736 -376
- package/dist/proxy/requestLogger.d.ts +1 -0
- package/dist/proxy/requestLogger.js +107 -5
- package/dist/proxy/restartControl.js +9 -4
- package/dist/proxy/rollingWorkerProcess.js +65 -0
- package/dist/proxy/rollingWorkerSupervisor.js +7 -0
- package/dist/proxy/updateCoordinator.d.ts +14 -1
- package/dist/proxy/updateCoordinator.js +57 -2
- package/dist/server/routes/claudeProxyRoutes.d.ts +4 -5
- package/dist/server/routes/claudeProxyRoutes.js +809 -577
- package/dist/server/routes/codexProxyRoutes.js +191 -9
- package/dist/server/routes/openaiProxyRoutes.d.ts +1 -1
- package/dist/server/routes/openaiProxyRoutes.js +261 -69
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/codex.d.ts +5 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/proxy.d.ts +148 -3
- package/dist/types/proxy.js +0 -15
- package/dist/types/proxyBudget.d.ts +74 -0
- package/dist/types/proxyBudget.js +1 -0
- package/dist/types/proxyContext.d.ts +35 -0
- package/dist/types/proxyContext.js +1 -0
- package/dist/utils/toolChoice.d.ts +19 -0
- package/dist/utils/toolChoice.js +39 -0
- package/docs/assets/dashboards/README.md +37 -0
- package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +197 -89
- package/docs-site/static/search-index.json +1 -1
- package/package.json +4 -1
- package/scripts/observability/NATIVE-DURABLE.md +63 -0
- package/scripts/observability/otel-collector.proxy-native-durable.yaml +93 -0
- package/scripts/observability/proxy-telemetry-backend.mjs +32 -5
- package/scripts/observability/proxy-telemetry-check.mjs +396 -61
- package/scripts/observability/query-proxy-history.mjs +34 -5
package/dist/types/proxy.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ProxyTokenBudgetSnapshot } from "./proxyBudget.js";
|
|
2
|
+
import type { ProxyContextEvidence, ProxyPreparedContext } from "./proxyContext.js";
|
|
1
3
|
/**
|
|
2
4
|
* Proxy type definitions for NeuroLink
|
|
3
5
|
*
|
|
@@ -556,6 +558,27 @@ export type ProxyAccountSortMetrics = {
|
|
|
556
558
|
scopedSaturated: boolean;
|
|
557
559
|
};
|
|
558
560
|
export type RequestLogEntry = {
|
|
561
|
+
/** Whether model identifies observed serving output rather than a routing target. */
|
|
562
|
+
servingModelStatus?: "observed" | "unavailable";
|
|
563
|
+
/** Whether retrying unchanged input can resolve this terminal failure. */
|
|
564
|
+
retryable?: boolean;
|
|
565
|
+
tokenBudget?: ProxyTokenBudgetSnapshot;
|
|
566
|
+
contextPreflight?: ProxyContextEvidence;
|
|
567
|
+
/** Internal adapter requests link to their one client-facing parent. */
|
|
568
|
+
parentRequestId?: string;
|
|
569
|
+
accountingScope?: "client" | "internal";
|
|
570
|
+
/** Exactly this request owns usage; bridge parents never duplicate it. */
|
|
571
|
+
usageOwnerRequestId?: string;
|
|
572
|
+
/** API price-table estimate, never a subscription charge or quota measure.
|
|
573
|
+
* Null when complete usage and an exact model rate are not both available. */
|
|
574
|
+
apiEquivalentCostUsd?: number | null;
|
|
575
|
+
/** API-equivalent cache-read savings against uncached input at the same rate. */
|
|
576
|
+
apiEquivalentCacheSavingsUsd?: number | null;
|
|
577
|
+
pricingStatus?: "exact" | "inferred" | "unavailable" | "usage_incomplete" | "owned_by_child";
|
|
578
|
+
pricingBasis?: "api_price_table";
|
|
579
|
+
pricingProvider?: string;
|
|
580
|
+
/** Credential identity is unavailable when the SDK does not expose it. */
|
|
581
|
+
accountIdentityStatus?: "observed" | "unavailable";
|
|
559
582
|
/** First text, refusal or populated tool output, excluding SSE control frames. */
|
|
560
583
|
firstUsefulOutputMs?: number;
|
|
561
584
|
/** Why first-useful-output timing is absent; never substitute a zero. */
|
|
@@ -646,7 +669,10 @@ export type ProcessedProxyBodyCapture = {
|
|
|
646
669
|
headers?: Record<string, string>;
|
|
647
670
|
stored: StoredBodyArtifact;
|
|
648
671
|
error?: string;
|
|
672
|
+
/** Admission plus worker queue time; retained for existing consumers. */
|
|
649
673
|
queueWaitMs?: number;
|
|
674
|
+
admissionWaitMs?: number;
|
|
675
|
+
workerQueueWaitMs?: number;
|
|
650
676
|
processingMs?: number;
|
|
651
677
|
admission?: ProxyBodyCaptureAdmission;
|
|
652
678
|
};
|
|
@@ -662,6 +688,11 @@ export type ProxyBodyCaptureAdmission = {
|
|
|
662
688
|
maxPendingBytes: number;
|
|
663
689
|
};
|
|
664
690
|
export type ProxyBodyCaptureWorkerSnapshot = {
|
|
691
|
+
processing?: number;
|
|
692
|
+
publishing?: number;
|
|
693
|
+
oldestAdmissionWaitMs?: number;
|
|
694
|
+
oldestProcessingMs?: number;
|
|
695
|
+
oldestPublicationMs?: number;
|
|
665
696
|
attempted: number;
|
|
666
697
|
completed: number;
|
|
667
698
|
rejected: number;
|
|
@@ -686,6 +717,13 @@ export type ProxyBodyDeliveryResult = {
|
|
|
686
717
|
unconfirmedChunks: number;
|
|
687
718
|
droppedChunks: number;
|
|
688
719
|
notSubmittedChunks?: number;
|
|
720
|
+
/** Total publication elapsed time, including capacity and exporter waits. */
|
|
721
|
+
publicationMs?: number;
|
|
722
|
+
capacityWaitMs?: number;
|
|
723
|
+
/** Maximum per-chunk residence before an OTLP export starts. */
|
|
724
|
+
maxChunkQueueWaitMs?: number;
|
|
725
|
+
/** Maximum per-chunk transport duration; never sums overlapping exports. */
|
|
726
|
+
maxChunkExportMs?: number;
|
|
689
727
|
reason?: string;
|
|
690
728
|
};
|
|
691
729
|
/** One bounded body publication, tracked across exporter callbacks. */
|
|
@@ -694,6 +732,8 @@ export type ProxyBodyPublicationProgress = {
|
|
|
694
732
|
unconfirmed: number;
|
|
695
733
|
dropped: number;
|
|
696
734
|
emitted: number;
|
|
735
|
+
maxChunkQueueWaitMs?: number;
|
|
736
|
+
maxChunkExportMs?: number;
|
|
697
737
|
notify?: () => void;
|
|
698
738
|
};
|
|
699
739
|
/** Bounded metadata-only evidence for an unconfirmed or locally rejected OTLP batch. */
|
|
@@ -714,6 +754,8 @@ export type ProxyTelemetryBackend = {
|
|
|
714
754
|
baseUrl: string;
|
|
715
755
|
organization: string;
|
|
716
756
|
stream: string;
|
|
757
|
+
/** Optional separate backend storage for bulk body chunks. */
|
|
758
|
+
bodyStream?: string;
|
|
717
759
|
authorization?: string;
|
|
718
760
|
collectorMetricsUrl?: string;
|
|
719
761
|
};
|
|
@@ -741,6 +783,10 @@ export type ProxyTelemetryDoctorOptions = {
|
|
|
741
783
|
endTime: number;
|
|
742
784
|
proxyUrl?: string;
|
|
743
785
|
maxRows?: number;
|
|
786
|
+
/** Explicit age-search horizon; older admissions are outside this proof. */
|
|
787
|
+
admissionLookbackMs?: number;
|
|
788
|
+
requestTimeoutMs?: number;
|
|
789
|
+
ingestionGraceMs?: number;
|
|
744
790
|
fetchImpl?: typeof fetch;
|
|
745
791
|
};
|
|
746
792
|
export type ProxyTelemetryCheck = {
|
|
@@ -766,6 +812,8 @@ export type ProxyTelemetryStoredRecord = Partial<RequestLogEntry> & {
|
|
|
766
812
|
event?: string;
|
|
767
813
|
telemetryStatus?: string;
|
|
768
814
|
outcomeSource?: string;
|
|
815
|
+
requestTimeoutMs?: number;
|
|
816
|
+
redactionLossy?: boolean;
|
|
769
817
|
};
|
|
770
818
|
export type ProxyTelemetryFieldCoverage = {
|
|
771
819
|
records: number;
|
|
@@ -786,6 +834,14 @@ export type ProxyRequestLoggerSnapshot = {
|
|
|
786
834
|
debug: ProxyRequestLogSinkSnapshot;
|
|
787
835
|
};
|
|
788
836
|
export type RequestAttemptLogEntry = {
|
|
837
|
+
/** False for a local context/budget refusal before a provider call. */
|
|
838
|
+
upstreamDispatched?: boolean;
|
|
839
|
+
/** Client model name retained independently of the dispatched model. */
|
|
840
|
+
requestedModel?: string;
|
|
841
|
+
tokenBudget?: ProxyTokenBudgetSnapshot;
|
|
842
|
+
contextPreflight?: ProxyContextEvidence;
|
|
843
|
+
accountingScope?: "client" | "internal";
|
|
844
|
+
usageOwnerRequestId?: string;
|
|
789
845
|
timestamp: string;
|
|
790
846
|
requestId: string;
|
|
791
847
|
/** Parent client request for an internal fallback invocation. */
|
|
@@ -825,10 +881,14 @@ export type RequestAttemptLogEntry = {
|
|
|
825
881
|
quotaResetAt?: number;
|
|
826
882
|
/** Unknown scope never implies an account-wide long-window rejection. */
|
|
827
883
|
quotaScope?: "session" | "weekly" | "unknown";
|
|
884
|
+
/** True when input includes the cache breakdown (native Codex wire usage). */
|
|
885
|
+
inputIncludesCachedTokens?: boolean;
|
|
828
886
|
inputTokens?: number;
|
|
829
887
|
outputTokens?: number;
|
|
830
888
|
cacheCreationTokens?: number;
|
|
831
889
|
cacheReadTokens?: number;
|
|
890
|
+
/** Reasoning tokens are a subset of output, never additional usage. */
|
|
891
|
+
reasoningTokens?: number;
|
|
832
892
|
/** Provider that received this upstream attempt. */
|
|
833
893
|
provider?: string;
|
|
834
894
|
/** OTel trace ID for correlation with distributed traces */
|
|
@@ -839,9 +899,9 @@ export type RequestAttemptLogEntry = {
|
|
|
839
899
|
traceFlags?: number;
|
|
840
900
|
};
|
|
841
901
|
/** Additional fields recorded when a Codex response becomes client-final. */
|
|
842
|
-
export type CodexFinalLogExtra = Partial<Pick<RequestLogEntry, "errorType" | "errorMessage" | "errorCode" | "transportScope" | "inputTokens" | "outputTokens" | "cacheReadTokens" | "cacheCreationTokens" | "reasoningTokens" | "terminalOutcome" | "firstUsefulOutputMs" | "firstUsefulOutputStatus" | "firstUsefulOutputEvent">>;
|
|
902
|
+
export type CodexFinalLogExtra = Partial<Pick<RequestLogEntry, "errorType" | "errorMessage" | "errorCode" | "transportScope" | "inputTokens" | "outputTokens" | "cacheReadTokens" | "cacheCreationTokens" | "reasoningTokens" | "terminalOutcome" | "firstUsefulOutputMs" | "firstUsefulOutputStatus" | "firstUsefulOutputEvent" | "retryable">>;
|
|
843
903
|
/** Additional fields recorded for each upstream Codex account attempt. */
|
|
844
|
-
export type CodexAttemptLogExtra = Partial<Pick<RequestAttemptLogEntry, "errorType" | "errorMessage" | "errorCode" | "transportScope" | "retryable" | "rateLimitKind" | "cooldownReason" | "quotaResetAt" | "quotaScope">>;
|
|
904
|
+
export type CodexAttemptLogExtra = Partial<Pick<RequestAttemptLogEntry, "inputTokens" | "outputTokens" | "cacheReadTokens" | "cacheCreationTokens" | "reasoningTokens" | "inputIncludesCachedTokens" | "errorType" | "errorMessage" | "errorCode" | "transportScope" | "retryable" | "rateLimitKind" | "cooldownReason" | "quotaResetAt" | "quotaScope">>;
|
|
845
905
|
/** Minimal persistence contract needed by Codex rotating-token refreshes. */
|
|
846
906
|
export type CodexRefreshTokenStore = {
|
|
847
907
|
peekTokens(provider: string): Promise<StoredOAuthTokens | null>;
|
|
@@ -876,6 +936,7 @@ export type ClaudeFinalRequestLogger = (status: number, accountLabel: string, ac
|
|
|
876
936
|
cacheReadTokens?: number;
|
|
877
937
|
reasoningTokens?: number;
|
|
878
938
|
errorCode?: string;
|
|
939
|
+
retryable?: boolean;
|
|
879
940
|
transportScope?: ProxyTransportScope;
|
|
880
941
|
}) => void;
|
|
881
942
|
export type ClaudeLoggedErrorBuilder = (status: number, message: string, errorType?: string, extra?: {
|
|
@@ -883,6 +944,7 @@ export type ClaudeLoggedErrorBuilder = (status: number, message: string, errorTy
|
|
|
883
944
|
accountType?: string;
|
|
884
945
|
attempt?: number;
|
|
885
946
|
errorCode?: string;
|
|
947
|
+
retryable?: boolean;
|
|
886
948
|
transportScope?: ProxyTransportScope;
|
|
887
949
|
}) => ClaudeErrorResponse;
|
|
888
950
|
export type ClaudeRequestRuntimeContext = {
|
|
@@ -935,6 +997,7 @@ export type AnthropicLoopState = {
|
|
|
935
997
|
export type AnthropicUpstreamBody = {
|
|
936
998
|
bodyStr: string;
|
|
937
999
|
sessionId?: string;
|
|
1000
|
+
preparedContext: ProxyPreparedContext<ClaudeRequest>;
|
|
938
1001
|
};
|
|
939
1002
|
export type AnthropicUpstreamBodyBuilder = (token: string) => AnthropicUpstreamBody;
|
|
940
1003
|
export type LoadedClaudeAccountContext = {
|
|
@@ -1044,6 +1107,7 @@ export type PreparedAnthropicAccountAttempt = {
|
|
|
1044
1107
|
headers?: Record<string, string>;
|
|
1045
1108
|
buildUpstreamBody?: AnthropicUpstreamBodyBuilder;
|
|
1046
1109
|
finalBodyStr?: string;
|
|
1110
|
+
preparedContext?: ProxyPreparedContext<ClaudeRequest>;
|
|
1047
1111
|
fetchStartMs?: number;
|
|
1048
1112
|
upstreamSpan?: Span;
|
|
1049
1113
|
};
|
|
@@ -1681,8 +1745,12 @@ export type ProxyRequestContext = {
|
|
|
1681
1745
|
* every non-Anthropic model prices to $0.
|
|
1682
1746
|
*/
|
|
1683
1747
|
provider?: string;
|
|
1684
|
-
/**
|
|
1748
|
+
/** Whether this span owns client request, latency, and error metrics. */
|
|
1685
1749
|
recordRequestMetrics?: boolean;
|
|
1750
|
+
/** Provider usage ownership. Defaults to recordRequestMetrics for backwards
|
|
1751
|
+
* compatibility: internal Codex fallback spans suppress both, while bridge
|
|
1752
|
+
* children explicitly retain usage and their parent owns client outcomes. */
|
|
1753
|
+
recordUsageMetrics?: boolean;
|
|
1686
1754
|
};
|
|
1687
1755
|
/** Response-side details parsed from the upstream reply (model, finish, tools). */
|
|
1688
1756
|
export type ResponseInfoContext = {
|
|
@@ -1722,6 +1790,11 @@ export type UsageContext = {
|
|
|
1722
1790
|
rateLimitAfter5h?: number;
|
|
1723
1791
|
rateLimitAfter7d?: number;
|
|
1724
1792
|
};
|
|
1793
|
+
/** Keep trace usage when a child request owns its token and cost metrics. */
|
|
1794
|
+
export type ProxyUsageAttributionOptions = {
|
|
1795
|
+
/** Defaults to the tracer's configured ownership when omitted. */
|
|
1796
|
+
recordMetrics?: boolean;
|
|
1797
|
+
};
|
|
1725
1798
|
/** Where a proxy env file path was sourced from. */
|
|
1726
1799
|
export type ProxyEnvSource = "cli" | "environment" | "default" | "none";
|
|
1727
1800
|
/** Result of resolving which proxy env file to load. */
|
|
@@ -1765,6 +1838,11 @@ export type ProxyActivitySnapshot = {
|
|
|
1765
1838
|
export type ProxyRuntimeActivity = {
|
|
1766
1839
|
activeRequests: number;
|
|
1767
1840
|
lastActivityAt: string | null;
|
|
1841
|
+
/** Supervisor-wide resources that must settle before process replacement. */
|
|
1842
|
+
drainingWorkers?: number;
|
|
1843
|
+
queuedSockets?: number;
|
|
1844
|
+
pendingTransfers?: number;
|
|
1845
|
+
candidateWorkers?: number;
|
|
1768
1846
|
};
|
|
1769
1847
|
/** Terminal state observed while the HTTP adapter relays a response body. */
|
|
1770
1848
|
export type ProxyResponseTerminalOutcome = "completed" | "bodyless" | "client_cancelled" | "stream_error";
|
|
@@ -1790,6 +1868,13 @@ export type ProxyLifecycleEventName = "runtime_sample" | "supervisor_event" | "r
|
|
|
1790
1868
|
export type ProxyLifecycleTerminalOutcome = ProxyResponseTerminalOutcome | "handler_error" | "unknown";
|
|
1791
1869
|
/** Content-free lifecycle event accepted by the bounded metadata logger. */
|
|
1792
1870
|
export type ProxyLifecycleEventInput = {
|
|
1871
|
+
/** Hard admission-to-terminal deadline used by reconciliation. */
|
|
1872
|
+
requestTimeoutMs?: number;
|
|
1873
|
+
/** Internal adapter requests link to their one client-facing parent. */
|
|
1874
|
+
parentRequestId?: string;
|
|
1875
|
+
accountingScope?: "client" | "internal";
|
|
1876
|
+
/** Exactly this request owns usage; bridge parents never duplicate it. */
|
|
1877
|
+
usageOwnerRequestId?: string;
|
|
1793
1878
|
event: ProxyLifecycleEventName;
|
|
1794
1879
|
requestId: string;
|
|
1795
1880
|
method: string;
|
|
@@ -1976,6 +2061,9 @@ export type ProxyAnalysisReport = {
|
|
|
1976
2061
|
providerFinalRecorded: boolean;
|
|
1977
2062
|
}>;
|
|
1978
2063
|
accepted: number;
|
|
2064
|
+
internalAccepted?: number;
|
|
2065
|
+
internalTerminal?: number;
|
|
2066
|
+
internalUnsettled?: number;
|
|
1979
2067
|
/** Accepted metadata requests that do not require model final records. */
|
|
1980
2068
|
auxiliaryRequests: number;
|
|
1981
2069
|
headers: number;
|
|
@@ -1988,6 +2076,8 @@ export type ProxyAnalysisReport = {
|
|
|
1988
2076
|
};
|
|
1989
2077
|
requests: {
|
|
1990
2078
|
completed: number;
|
|
2079
|
+
/** Linked internal adapter finals excluded from client request totals. */
|
|
2080
|
+
internalCompleted?: number;
|
|
1991
2081
|
success: number;
|
|
1992
2082
|
errors: number;
|
|
1993
2083
|
finalRateLimits: number;
|
|
@@ -2075,6 +2165,9 @@ export type ProxyAnalysisAttemptRecord = {
|
|
|
2075
2165
|
};
|
|
2076
2166
|
/** Final request fields retained while joining offline proxy log records. */
|
|
2077
2167
|
export type ProxyAnalysisFinalRequestRecord = {
|
|
2168
|
+
accountingScope?: "client" | "internal";
|
|
2169
|
+
parentRequestId?: string;
|
|
2170
|
+
usageOwnerRequestId?: string;
|
|
2078
2171
|
firstUsefulOutputMs: number | null;
|
|
2079
2172
|
timestamp: string;
|
|
2080
2173
|
status: number;
|
|
@@ -2114,6 +2207,9 @@ export type ProxyCancellableTransformer<I, O> = Transformer<I, O> & {
|
|
|
2114
2207
|
* a null result as "not observed", never as "zero tokens".
|
|
2115
2208
|
*/
|
|
2116
2209
|
export type CodexStreamUsage = {
|
|
2210
|
+
/** Distinguish missing provider usage from a reported zero. */
|
|
2211
|
+
inputTokensObserved?: boolean;
|
|
2212
|
+
outputTokensObserved?: boolean;
|
|
2117
2213
|
inputTokens: number;
|
|
2118
2214
|
outputTokens: number;
|
|
2119
2215
|
cacheReadTokens: number;
|
|
@@ -2146,6 +2242,12 @@ export type ProxyAnalysisRoutingRecord = {
|
|
|
2146
2242
|
};
|
|
2147
2243
|
/** Request metadata retained by the HTTP adapter for terminal error logging. */
|
|
2148
2244
|
export type RuntimeRequestMetadata = {
|
|
2245
|
+
/** Internal adapter requests link to their one client-facing parent. */
|
|
2246
|
+
parentRequestId?: string;
|
|
2247
|
+
accountingScope?: "client" | "internal";
|
|
2248
|
+
/** Exactly this request owns usage; bridge parents never duplicate it. */
|
|
2249
|
+
usageOwnerRequestId?: string;
|
|
2250
|
+
abortController?: AbortController;
|
|
2149
2251
|
/** Last dispatched attempt, retained until this HTTP request terminates. */
|
|
2150
2252
|
lastUpstreamAttempt?: RequestAttemptLogEntry;
|
|
2151
2253
|
requestId: string;
|
|
@@ -2331,6 +2433,14 @@ export type CompareProxyReplayOptions = {
|
|
|
2331
2433
|
};
|
|
2332
2434
|
/** Persisted artifact produced when a body is stored to disk. */
|
|
2333
2435
|
export type StoredBodyArtifact = {
|
|
2436
|
+
/** Bytes supplied to redaction, after any source observer limit. */
|
|
2437
|
+
inputRetainedBytes?: number;
|
|
2438
|
+
inputEncoding?: "utf8_text" | "structured_object";
|
|
2439
|
+
sourceTruncated?: boolean;
|
|
2440
|
+
processingTruncated?: boolean;
|
|
2441
|
+
/** Invalid structured frames were removed for secret safety. */
|
|
2442
|
+
redactionLossy?: boolean;
|
|
2443
|
+
unparseableRedactedFrames?: number;
|
|
2334
2444
|
bodyPath?: string;
|
|
2335
2445
|
bodySha256?: string;
|
|
2336
2446
|
redactedBodyBytes?: number;
|
|
@@ -4029,4 +4139,39 @@ type ProxyBodyCaptureWaitingSnapshot = {
|
|
|
4029
4139
|
admissionWaits: number;
|
|
4030
4140
|
admissionTimeouts: number;
|
|
4031
4141
|
};
|
|
4142
|
+
/** Immutable package selection used by the proxy launcher, never by global npm. */
|
|
4143
|
+
export type ProxyPackageSelection = {
|
|
4144
|
+
version: string;
|
|
4145
|
+
entryScript: string;
|
|
4146
|
+
nodePath: string;
|
|
4147
|
+
};
|
|
4148
|
+
/** Atomically published active and rollback package selections. */
|
|
4149
|
+
export type ProxyPackageSelectionState = {
|
|
4150
|
+
schemaVersion: 1;
|
|
4151
|
+
active: ProxyPackageSelection | null;
|
|
4152
|
+
previous: ProxyPackageSelection | null;
|
|
4153
|
+
};
|
|
4154
|
+
/** Bounded asynchronous package installation with injectable subprocesses. */
|
|
4155
|
+
export type ProxyStagedInstallOptions = {
|
|
4156
|
+
version: string;
|
|
4157
|
+
packagesDir: string;
|
|
4158
|
+
installer: Pick<GlobalInstallerProbe, "kind" | "bin">;
|
|
4159
|
+
idleTimeoutMs?: number;
|
|
4160
|
+
maxDurationMs?: number;
|
|
4161
|
+
killGraceMs?: number;
|
|
4162
|
+
spawn?: typeof import("node:child_process").spawn;
|
|
4163
|
+
execFileSync?: GlobalInstallerExecFile;
|
|
4164
|
+
onProgress?: (progress: {
|
|
4165
|
+
elapsedMs: number;
|
|
4166
|
+
outputBytes: number;
|
|
4167
|
+
}) => void;
|
|
4168
|
+
};
|
|
4169
|
+
/** Saved launchd settings retained when installing the service again. */
|
|
4170
|
+
export type ProxyServiceInstallSettings = {
|
|
4171
|
+
envFile?: string;
|
|
4172
|
+
configFile?: string;
|
|
4173
|
+
host?: string;
|
|
4174
|
+
port?: number;
|
|
4175
|
+
environment: Record<string, string>;
|
|
4176
|
+
};
|
|
4032
4177
|
export {};
|
package/dist/types/proxy.js
CHANGED
|
@@ -1,16 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Proxy type definitions for NeuroLink
|
|
3
|
-
*
|
|
4
|
-
* Consolidates all proxy-related types from:
|
|
5
|
-
* - src/lib/proxy/claudeFormat.ts (Claude API types, SSE types, internal result)
|
|
6
|
-
* - src/lib/proxy/cloaking/types.ts (cloaking pipeline types)
|
|
7
|
-
* - src/lib/proxy/cloaking/plugins/ (plugin option types)
|
|
8
|
-
* - src/lib/proxy/modelRouter.ts (routing types)
|
|
9
|
-
* - src/lib/proxy/proxyConfig.ts (config file types)
|
|
10
|
-
* - src/lib/proxy/requestLogger.ts (log entry type)
|
|
11
|
-
* - src/lib/proxy/usageStats.ts (stats types)
|
|
12
|
-
* - src/lib/proxy/tokenRefresh.ts (refresh types)
|
|
13
|
-
* - src/lib/proxy/accountQuota.ts (quota type)
|
|
14
|
-
* - src/lib/server/routes/claudeProxyRoutes.ts (runtime state, deps)
|
|
15
|
-
*/
|
|
16
1
|
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Operator-configured proxy token caps. All caps are optional and disabled by default. */
|
|
2
|
+
export type ProxyTokenBudgetPolicy = {
|
|
3
|
+
/** Per-provider/account estimated outstanding input plus maximum output. */
|
|
4
|
+
maxInFlightTokens?: number;
|
|
5
|
+
/** Charged tokens in the account's fixed window, including outstanding reservations. */
|
|
6
|
+
accountWindowTokens?: number;
|
|
7
|
+
/** Charged tokens across providers/accounts sharing the same client session. */
|
|
8
|
+
sessionWindowTokens?: number;
|
|
9
|
+
windowMs?: number;
|
|
10
|
+
};
|
|
11
|
+
export type ProxyTokenBudgetReservation = {
|
|
12
|
+
provider: string;
|
|
13
|
+
accountKey: string;
|
|
14
|
+
sessionKey: string;
|
|
15
|
+
requestId: string;
|
|
16
|
+
reservationTokens: number;
|
|
17
|
+
policy?: ProxyTokenBudgetPolicy;
|
|
18
|
+
estimateProvenance?: string;
|
|
19
|
+
};
|
|
20
|
+
export type ProxyTokenBudgetSnapshot = {
|
|
21
|
+
scope: "disabled" | "process" | "supervisor";
|
|
22
|
+
reservationTokens: number;
|
|
23
|
+
accountChargedTokens: number;
|
|
24
|
+
accountInFlightTokens: number;
|
|
25
|
+
sessionChargedTokens: number;
|
|
26
|
+
sessionInFlightTokens: number;
|
|
27
|
+
windowMs: number;
|
|
28
|
+
estimateProvenance: string;
|
|
29
|
+
settlement?: "provider_reported" | "estimate_retained" | "cancelled_before_dispatch" | "unconfirmed";
|
|
30
|
+
};
|
|
31
|
+
export type ProxyTokenBudgetLease = {
|
|
32
|
+
leaseId: string;
|
|
33
|
+
snapshot: ProxyTokenBudgetSnapshot;
|
|
34
|
+
settle: (actualTotalTokens?: number) => Promise<void>;
|
|
35
|
+
cancelBeforeDispatch: () => Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
export type ProxyTokenBudgetBucket = {
|
|
38
|
+
chargedTokens: number;
|
|
39
|
+
inFlightTokens: number;
|
|
40
|
+
startedAt: number;
|
|
41
|
+
windowMs: number;
|
|
42
|
+
lastTouched: number;
|
|
43
|
+
};
|
|
44
|
+
export type ProxyTokenBudgetRecord = {
|
|
45
|
+
owner: string;
|
|
46
|
+
accountKey: string;
|
|
47
|
+
sessionKey: string;
|
|
48
|
+
estimate: number;
|
|
49
|
+
snapshot: ProxyTokenBudgetSnapshot;
|
|
50
|
+
};
|
|
51
|
+
export type ProxyTokenBudgetRpcRequest = {
|
|
52
|
+
type: "proxy-budget:request";
|
|
53
|
+
rpcId: string;
|
|
54
|
+
pid: number;
|
|
55
|
+
generation: number;
|
|
56
|
+
action: "reserve" | "settle" | "cancel";
|
|
57
|
+
reservation?: ProxyTokenBudgetReservation;
|
|
58
|
+
leaseId?: string;
|
|
59
|
+
actualTotalTokens?: number;
|
|
60
|
+
};
|
|
61
|
+
export type ProxyTokenBudgetRpcResponse = {
|
|
62
|
+
type: "proxy-budget:response";
|
|
63
|
+
rpcId: string;
|
|
64
|
+
ok: boolean;
|
|
65
|
+
leaseId?: string;
|
|
66
|
+
snapshot?: ProxyTokenBudgetSnapshot;
|
|
67
|
+
errorCode?: string;
|
|
68
|
+
error?: string;
|
|
69
|
+
};
|
|
70
|
+
export type ProxyTokenBudgetRpcPending = {
|
|
71
|
+
resolve: (result: ProxyTokenBudgetRpcResponse) => void;
|
|
72
|
+
reject: (error: Error) => void;
|
|
73
|
+
timer: NodeJS.Timeout;
|
|
74
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Explicit proxy context controls; all token quantities remain estimates. */
|
|
2
|
+
export type ProxyContextPolicy = {
|
|
3
|
+
maxInputTokens?: number;
|
|
4
|
+
outputReserveTokens?: number;
|
|
5
|
+
enforceDiscoveredLimits?: boolean;
|
|
6
|
+
toolAllowlist?: string[];
|
|
7
|
+
models?: Record<string, {
|
|
8
|
+
contextWindow: number;
|
|
9
|
+
maxOutputTokens?: number;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
export type ProxyContextEvidence = {
|
|
13
|
+
provider: string;
|
|
14
|
+
model: string;
|
|
15
|
+
inputTokensEstimate: number;
|
|
16
|
+
toolsTokensEstimate: number;
|
|
17
|
+
instructionsTokensEstimate: number;
|
|
18
|
+
schemaTokensEstimate: number;
|
|
19
|
+
outputTokensReserve: number;
|
|
20
|
+
reasoningIncludedInOutputReserve: true;
|
|
21
|
+
contextWindow?: number;
|
|
22
|
+
contextLimitSource: "configured" | "discovered" | "unknown";
|
|
23
|
+
tokenCountSource: "estimated";
|
|
24
|
+
multimodalEstimate: boolean;
|
|
25
|
+
originalToolCount: number;
|
|
26
|
+
retainedToolCount: number;
|
|
27
|
+
historyModified: false;
|
|
28
|
+
};
|
|
29
|
+
export type ProxyPreparedContext<T> = {
|
|
30
|
+
body: T;
|
|
31
|
+
inputTokensEstimate: number;
|
|
32
|
+
outputTokensReserve: number;
|
|
33
|
+
totalTokensReservation: number;
|
|
34
|
+
evidence: ProxyContextEvidence;
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,3 +2,22 @@ import type { Tool, ToolChoice } from "../types/index.js";
|
|
|
2
2
|
export declare function resolveToolChoice(options: {
|
|
3
3
|
toolChoice?: ToolChoice<Record<string, Tool>>;
|
|
4
4
|
}, tools: Record<string, Tool> | undefined, shouldUseTools: boolean): ToolChoice<Record<string, Tool>> | "none";
|
|
5
|
+
/**
|
|
6
|
+
* Normalise an already-resolved tool choice into the shape
|
|
7
|
+
* `toolChoiceToAnthropic` (and its peers) accept.
|
|
8
|
+
*
|
|
9
|
+
* A provider's model handle sees a value that has *already* been through
|
|
10
|
+
* `resolveToolChoice` upstream, so it must translate rather than re-resolve —
|
|
11
|
+
* re-resolving there would apply the `auto`/`none` defaulting twice, and the
|
|
12
|
+
* `tools`/`shouldUseTools` inputs it needs are not in scope at that layer
|
|
13
|
+
* anyway.
|
|
14
|
+
*
|
|
15
|
+
* What does vary is the shape. The value arrives either as a bare string
|
|
16
|
+
* (`"auto"` / `"none"` / `"required"`), as `{ type: "tool", toolName }`, or —
|
|
17
|
+
* on the native loop's tool-free re-ask — as an injected `{ type: "none" }`
|
|
18
|
+
* object. Reading `.type` off the string forms yields `undefined`, which is
|
|
19
|
+
* how string choices were silently dropped from the Anthropic generate
|
|
20
|
+
* request. This exists so that normalisation lives in one place instead of
|
|
21
|
+
* being re-derived at each call site.
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeResolvedToolChoice(value: unknown): ToolChoice<Record<string, Tool>> | undefined;
|
package/dist/utils/toolChoice.js
CHANGED
|
@@ -4,3 +4,42 @@ export function resolveToolChoice(options, tools, shouldUseTools) {
|
|
|
4
4
|
}
|
|
5
5
|
return options.toolChoice ?? "auto";
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Normalise an already-resolved tool choice into the shape
|
|
9
|
+
* `toolChoiceToAnthropic` (and its peers) accept.
|
|
10
|
+
*
|
|
11
|
+
* A provider's model handle sees a value that has *already* been through
|
|
12
|
+
* `resolveToolChoice` upstream, so it must translate rather than re-resolve —
|
|
13
|
+
* re-resolving there would apply the `auto`/`none` defaulting twice, and the
|
|
14
|
+
* `tools`/`shouldUseTools` inputs it needs are not in scope at that layer
|
|
15
|
+
* anyway.
|
|
16
|
+
*
|
|
17
|
+
* What does vary is the shape. The value arrives either as a bare string
|
|
18
|
+
* (`"auto"` / `"none"` / `"required"`), as `{ type: "tool", toolName }`, or —
|
|
19
|
+
* on the native loop's tool-free re-ask — as an injected `{ type: "none" }`
|
|
20
|
+
* object. Reading `.type` off the string forms yields `undefined`, which is
|
|
21
|
+
* how string choices were silently dropped from the Anthropic generate
|
|
22
|
+
* request. This exists so that normalisation lives in one place instead of
|
|
23
|
+
* being re-derived at each call site.
|
|
24
|
+
*/
|
|
25
|
+
export function normalizeResolvedToolChoice(value) {
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if (value && typeof value === "object") {
|
|
30
|
+
const candidate = value;
|
|
31
|
+
if (candidate.type === "tool") {
|
|
32
|
+
// A tool choice that names no tool cannot be honoured. Fall through to
|
|
33
|
+
// the provider default rather than emitting the bare string "tool",
|
|
34
|
+
// which is not one of the valid auto/none/required forms and would be
|
|
35
|
+
// rejected downstream.
|
|
36
|
+
return candidate.toolName
|
|
37
|
+
? { type: "tool", toolName: candidate.toolName }
|
|
38
|
+
: undefined;
|
|
39
|
+
}
|
|
40
|
+
if (candidate.type) {
|
|
41
|
+
return candidate.type;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Proxy dashboard accounting
|
|
2
|
+
|
|
3
|
+
Import `neurolink-proxy-observability-dashboard.json` into OpenObserve after
|
|
4
|
+
the matching proxy release emits the indexed accounting and pricing fields.
|
|
5
|
+
The template is an artifact; changing it does not update an installed dashboard
|
|
6
|
+
or activate a proxy release. Request panels query the metadata log stream.
|
|
7
|
+
Bulk body captures can use a separate stream.
|
|
8
|
+
|
|
9
|
+
Client traffic, errors, and latency belong to the outer request. An internal
|
|
10
|
+
bridge request owns provider usage when `proxy_usage_owner_request_id` names
|
|
11
|
+
that child; the parent contributes no second token charge. Cached input is
|
|
12
|
+
included once according to `ai_input_includes_cached_tokens`.
|
|
13
|
+
|
|
14
|
+
Log queries first collapse exact producer retries, ignoring ingestion-time
|
|
15
|
+
changes. The stable identity combines service instance and producer event ID;
|
|
16
|
+
older records fall back to request ID where available. Contradictory payloads
|
|
17
|
+
under one identity are excluded from aggregate totals. The Traffic & Health
|
|
18
|
+
conflict counter reports those exclusions. A nonzero counter means totals
|
|
19
|
+
cover only the verified subset; use telemetry doctor for the conflicting raw
|
|
20
|
+
payloads. Rows without any stable identity cannot establish retry deduplication.
|
|
21
|
+
|
|
22
|
+
Cost panels sum `ai_cost_api_equivalent_usd` only when
|
|
23
|
+
`ai_pricing_status='exact'`. These are estimates from this release's API price
|
|
24
|
+
table and complete provider usage, **not subscription charges or quota usage**.
|
|
25
|
+
Prefix-inferred prices, missing rates, partial token usage, and a bridge parent's
|
|
26
|
+
delegated usage do not receive an invented dollar amount. Pricing-gap panels
|
|
27
|
+
show the excluded usage owners; a blank known-cost total is not zero spending.
|
|
28
|
+
Cache savings compare cache reads with the same table's uncached input rate.
|
|
29
|
+
The offline analyzer's historical estimates separately retain their existing
|
|
30
|
+
`requestsPricedByPrefix` and `modelsPricedByPrefix` disclosures.
|
|
31
|
+
|
|
32
|
+
The deterministic capture-pipeline suite executes the shipped request, token,
|
|
33
|
+
cost, and conflict SQL on an in-memory database and records exact price-table,
|
|
34
|
+
partial-usage, and unknown-model OTLP fixtures. SQLite validates the standard SQL
|
|
35
|
+
logic; verify field availability and query compatibility in the target
|
|
36
|
+
OpenObserve version when installing the dashboard. Metric panels retain their
|
|
37
|
+
scrape/window semantics and are not a substitute for reconciled request logs.
|