@jsonstudio/llms 0.6.567 → 0.6.586
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/dist/conversion/codecs/gemini-openai-codec.js +33 -4
- package/dist/conversion/codecs/openai-openai-codec.js +2 -1
- package/dist/conversion/codecs/responses-openai-codec.js +3 -2
- package/dist/conversion/compat/actions/glm-history-image-trim.d.ts +2 -0
- package/dist/conversion/compat/actions/glm-history-image-trim.js +88 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -2
- package/dist/conversion/hub/pipeline/hub-pipeline.js +72 -81
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +0 -34
- package/dist/conversion/hub/process/chat-process.js +68 -24
- package/dist/conversion/hub/response/provider-response.js +0 -8
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +22 -3
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +267 -14
- package/dist/conversion/hub/types/chat-envelope.d.ts +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -2
- package/dist/conversion/responses/responses-openai-bridge.js +1 -13
- package/dist/conversion/shared/anthropic-message-utils.js +54 -0
- package/dist/conversion/shared/args-mapping.js +11 -3
- package/dist/conversion/shared/responses-output-builder.js +42 -21
- package/dist/conversion/shared/streaming-text-extractor.d.ts +25 -0
- package/dist/conversion/shared/streaming-text-extractor.js +31 -38
- package/dist/conversion/shared/text-markup-normalizer.d.ts +20 -0
- package/dist/conversion/shared/text-markup-normalizer.js +118 -31
- package/dist/conversion/shared/tool-filter-pipeline.js +56 -30
- package/dist/conversion/shared/tool-harvester.js +43 -12
- package/dist/conversion/shared/tool-mapping.d.ts +1 -0
- package/dist/conversion/shared/tool-mapping.js +33 -19
- package/dist/filters/index.d.ts +1 -0
- package/dist/filters/index.js +1 -0
- package/dist/filters/special/request-tools-normalize.js +14 -4
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +23 -0
- package/dist/filters/special/response-apply-patch-toon-decode.js +117 -0
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +10 -0
- package/dist/filters/special/response-tool-arguments-toon-decode.js +154 -26
- package/dist/guidance/index.js +71 -42
- package/dist/router/virtual-router/bootstrap.js +10 -5
- package/dist/router/virtual-router/classifier.js +16 -7
- package/dist/router/virtual-router/engine-health.d.ts +11 -0
- package/dist/router/virtual-router/engine-health.js +217 -4
- package/dist/router/virtual-router/engine-logging.d.ts +2 -1
- package/dist/router/virtual-router/engine-logging.js +35 -3
- package/dist/router/virtual-router/engine.d.ts +17 -1
- package/dist/router/virtual-router/engine.js +184 -6
- package/dist/router/virtual-router/routing-instructions.d.ts +2 -0
- package/dist/router/virtual-router/routing-instructions.js +19 -1
- package/dist/router/virtual-router/tool-signals.d.ts +2 -1
- package/dist/router/virtual-router/tool-signals.js +324 -119
- package/dist/router/virtual-router/types.d.ts +31 -1
- package/dist/router/virtual-router/types.js +2 -2
- package/dist/servertool/engine.js +3 -0
- package/dist/servertool/handlers/iflow-model-error-retry.d.ts +1 -0
- package/dist/servertool/handlers/iflow-model-error-retry.js +93 -0
- package/dist/servertool/handlers/stop-message-auto.js +61 -4
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +27 -0
- package/dist/sse/json-to-sse/event-generators/responses.js +9 -2
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +23 -3
- package/dist/tools/apply-patch-structured.d.ts +20 -0
- package/dist/tools/apply-patch-structured.js +240 -0
- package/dist/tools/tool-description-utils.d.ts +5 -0
- package/dist/tools/tool-description-utils.js +50 -0
- package/dist/tools/tool-registry.js +11 -193
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import { parseRoutingInstructions, applyRoutingInstructions, cleanMessagesFromRo
|
|
|
10
10
|
import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync } from './sticky-session-store.js';
|
|
11
11
|
import { buildHitReason, formatVirtualRouterHit } from './engine-logging.js';
|
|
12
12
|
import { selectProviderImpl } from './engine-selection.js';
|
|
13
|
-
import { applySeriesCooldownImpl, handleProviderFailureImpl, mapProviderErrorImpl } from './engine-health.js';
|
|
13
|
+
import { applyQuotaDepletedImpl, applyQuotaRecoveryImpl, applySeriesCooldownImpl, handleProviderFailureImpl, mapProviderErrorImpl } from './engine-health.js';
|
|
14
14
|
export class VirtualRouterEngine {
|
|
15
15
|
routing = {};
|
|
16
16
|
providerRegistry = new ProviderRegistry();
|
|
@@ -26,7 +26,20 @@ export class VirtualRouterEngine {
|
|
|
26
26
|
statsCenter = getStatsCenter();
|
|
27
27
|
// Derived flags from VirtualRouterConfig/routing used by process / response layers.
|
|
28
28
|
webSearchForce = false;
|
|
29
|
+
healthStore;
|
|
30
|
+
routingStateStore = {
|
|
31
|
+
loadSync: loadRoutingInstructionStateSync,
|
|
32
|
+
saveAsync: saveRoutingInstructionStateAsync
|
|
33
|
+
};
|
|
29
34
|
routingInstructionState = new Map();
|
|
35
|
+
constructor(deps) {
|
|
36
|
+
if (deps?.healthStore) {
|
|
37
|
+
this.healthStore = deps.healthStore;
|
|
38
|
+
}
|
|
39
|
+
if (deps?.routingStateStore) {
|
|
40
|
+
this.routingStateStore = deps.routingStateStore;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
30
43
|
initialize(config) {
|
|
31
44
|
this.validateConfig(config);
|
|
32
45
|
this.routing = config.routing;
|
|
@@ -34,6 +47,8 @@ export class VirtualRouterEngine {
|
|
|
34
47
|
this.healthManager.configure(config.health);
|
|
35
48
|
this.healthConfig = config.health ?? null;
|
|
36
49
|
this.healthManager.registerProviders(Object.keys(config.providers));
|
|
50
|
+
this.providerCooldowns.clear();
|
|
51
|
+
this.restoreHealthFromStore();
|
|
37
52
|
this.loadBalancer = new RouteLoadBalancer(config.loadBalancing);
|
|
38
53
|
this.classifier = new RoutingClassifier(config.classifier);
|
|
39
54
|
this.contextRouting = config.contextRouting ?? { warnRatio: 0.9, hardLimit: false };
|
|
@@ -52,6 +67,15 @@ export class VirtualRouterEngine {
|
|
|
52
67
|
if (metadataInstructions.length > 0) {
|
|
53
68
|
routingState = applyRoutingInstructions(metadataInstructions, routingState);
|
|
54
69
|
}
|
|
70
|
+
const disableStickyRoutes = metadata &&
|
|
71
|
+
typeof metadata === 'object' &&
|
|
72
|
+
metadata.disableStickyRoutes === true;
|
|
73
|
+
if (disableStickyRoutes && routingState.stickyTarget) {
|
|
74
|
+
routingState = {
|
|
75
|
+
...routingState,
|
|
76
|
+
stickyTarget: undefined
|
|
77
|
+
};
|
|
78
|
+
}
|
|
55
79
|
const instructions = parseRoutingInstructions(request.messages);
|
|
56
80
|
if (instructions.length > 0) {
|
|
57
81
|
routingState = applyRoutingInstructions(instructions, routingState);
|
|
@@ -98,7 +122,7 @@ export class VirtualRouterEngine {
|
|
|
98
122
|
const hitReason = buildHitReason(selection.routeUsed, selection.providerKey, classification, features, routingMode, { providerRegistry: this.providerRegistry, contextRouting: this.contextRouting });
|
|
99
123
|
const stickyScope = routingMode !== 'none' ? this.resolveSessionScope(metadata) : undefined;
|
|
100
124
|
const routeForLog = routingMode === 'sticky' ? 'sticky' : selection.routeUsed;
|
|
101
|
-
const formatted = formatVirtualRouterHit(routeForLog, selection.poolId, selection.providerKey, target.modelId || '', hitReason, stickyScope);
|
|
125
|
+
const formatted = formatVirtualRouterHit(routeForLog, selection.poolId, selection.providerKey, target.modelId || '', hitReason, stickyScope, routingState);
|
|
102
126
|
if (formatted) {
|
|
103
127
|
this.debug?.log?.(formatted);
|
|
104
128
|
}
|
|
@@ -106,6 +130,36 @@ export class VirtualRouterEngine {
|
|
|
106
130
|
this.debug?.log?.('[virtual-router-hit]', selection.routeUsed, selection.providerKey, target.modelId || '', hitReason ? `reason=${hitReason}` : '');
|
|
107
131
|
}
|
|
108
132
|
const didFallback = selection.routeUsed !== requestedRoute;
|
|
133
|
+
// 自动 sticky:对需要上下文 save/restore 的 Responses 会话,强制同一个 provider.key.model。
|
|
134
|
+
// 其它协议不启用粘滞,仅显式 routing 指令才会写入 stickyTarget。
|
|
135
|
+
const providerProtocol = metadata?.providerProtocol;
|
|
136
|
+
const disableSticky = metadata &&
|
|
137
|
+
typeof metadata === 'object' &&
|
|
138
|
+
metadata.disableStickyRoutes === true;
|
|
139
|
+
const shouldAutoStickyForResponses = providerProtocol === 'openai-responses' && !disableSticky;
|
|
140
|
+
if (shouldAutoStickyForResponses) {
|
|
141
|
+
const stickyKeyForState = this.resolveStickyKey(metadata);
|
|
142
|
+
if (stickyKeyForState) {
|
|
143
|
+
const stateKey = stickyKeyForState;
|
|
144
|
+
const state = this.getRoutingInstructionState(stateKey);
|
|
145
|
+
if (!state.stickyTarget) {
|
|
146
|
+
const providerId = this.extractProviderId(selection.providerKey);
|
|
147
|
+
if (providerId) {
|
|
148
|
+
const parts = selection.providerKey.split('.');
|
|
149
|
+
const keyAlias = parts.length >= 3 ? parts[1] : undefined;
|
|
150
|
+
const modelId = target.modelId;
|
|
151
|
+
state.stickyTarget = {
|
|
152
|
+
provider: providerId,
|
|
153
|
+
keyAlias,
|
|
154
|
+
model: modelId,
|
|
155
|
+
// pathLength=3 表示 provider.key.model 形式,对应 alias 显式 sticky;
|
|
156
|
+
// 若缺少别名或模型,则退化为更短 pathLength。
|
|
157
|
+
pathLength: keyAlias && modelId ? 3 : keyAlias ? 2 : 1
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
109
163
|
return {
|
|
110
164
|
target,
|
|
111
165
|
decision: {
|
|
@@ -132,6 +186,24 @@ export class VirtualRouterEngine {
|
|
|
132
186
|
handleProviderFailureImpl(event, this.healthManager, this.providerHealthConfig(), (key, ttl) => this.markProviderCooldown(key, ttl));
|
|
133
187
|
}
|
|
134
188
|
handleProviderError(event) {
|
|
189
|
+
if (this.healthStore && typeof this.healthStore.recordProviderError === 'function') {
|
|
190
|
+
try {
|
|
191
|
+
this.healthStore.recordProviderError(event);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// ignore persistence errors
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// 配额恢复事件优先处理:一旦识别到 virtualRouterQuotaRecovery,
|
|
198
|
+
// 直接清理健康状态/冷却 TTL,避免继续走常规错误映射逻辑。
|
|
199
|
+
const handledByQuota = applyQuotaRecoveryImpl(event, this.healthManager, (key) => this.clearProviderCooldown(key), this.debug);
|
|
200
|
+
if (handledByQuota) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const handledByQuotaDepleted = applyQuotaDepletedImpl(event, this.healthManager, (key, ttl) => this.markProviderCooldown(key, ttl), this.debug);
|
|
204
|
+
if (handledByQuotaDepleted) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
135
207
|
applySeriesCooldownImpl(event, this.providerRegistry, this.healthManager, (key, ttl) => this.markProviderCooldown(key, ttl), this.debug);
|
|
136
208
|
const derived = mapProviderErrorImpl(event, this.providerHealthConfig());
|
|
137
209
|
if (!derived) {
|
|
@@ -262,7 +334,9 @@ export class VirtualRouterEngine {
|
|
|
262
334
|
disabledModels: new Map(),
|
|
263
335
|
stopMessageText: undefined,
|
|
264
336
|
stopMessageMaxRepeats: undefined,
|
|
265
|
-
stopMessageUsed: undefined
|
|
337
|
+
stopMessageUsed: undefined,
|
|
338
|
+
stopMessageUpdatedAt: undefined,
|
|
339
|
+
stopMessageLastUsedAt: undefined
|
|
266
340
|
};
|
|
267
341
|
}
|
|
268
342
|
this.routingInstructionState.set(key, initial);
|
|
@@ -763,17 +837,28 @@ export class VirtualRouterEngine {
|
|
|
763
837
|
const noDisabledProviders = state.disabledProviders.size === 0;
|
|
764
838
|
const noDisabledKeys = state.disabledKeys.size === 0;
|
|
765
839
|
const noDisabledModels = state.disabledModels.size === 0;
|
|
766
|
-
|
|
840
|
+
const noStopMessage = (!state.stopMessageText || !state.stopMessageText.trim()) &&
|
|
841
|
+
(typeof state.stopMessageMaxRepeats !== 'number' || !Number.isFinite(state.stopMessageMaxRepeats)) &&
|
|
842
|
+
(typeof state.stopMessageUsed !== 'number' || !Number.isFinite(state.stopMessageUsed)) &&
|
|
843
|
+
(typeof state.stopMessageUpdatedAt !== 'number' || !Number.isFinite(state.stopMessageUpdatedAt)) &&
|
|
844
|
+
(typeof state.stopMessageLastUsedAt !== 'number' || !Number.isFinite(state.stopMessageLastUsedAt));
|
|
845
|
+
return (noForced &&
|
|
846
|
+
noSticky &&
|
|
847
|
+
noAllowed &&
|
|
848
|
+
noDisabledProviders &&
|
|
849
|
+
noDisabledKeys &&
|
|
850
|
+
noDisabledModels &&
|
|
851
|
+
noStopMessage);
|
|
767
852
|
}
|
|
768
853
|
persistRoutingInstructionState(key, state) {
|
|
769
854
|
if (!key || (!key.startsWith('session:') && !key.startsWith('conversation:'))) {
|
|
770
855
|
return;
|
|
771
856
|
}
|
|
772
857
|
if (this.isRoutingStateEmpty(state)) {
|
|
773
|
-
|
|
858
|
+
this.routingStateStore.saveAsync(key, null);
|
|
774
859
|
return;
|
|
775
860
|
}
|
|
776
|
-
|
|
861
|
+
this.routingStateStore.saveAsync(key, state);
|
|
777
862
|
}
|
|
778
863
|
markProviderCooldown(providerKey, cooldownMs) {
|
|
779
864
|
if (!providerKey) {
|
|
@@ -784,6 +869,15 @@ export class VirtualRouterEngine {
|
|
|
784
869
|
return;
|
|
785
870
|
}
|
|
786
871
|
this.providerCooldowns.set(providerKey, Date.now() + ttl);
|
|
872
|
+
this.persistHealthSnapshot();
|
|
873
|
+
}
|
|
874
|
+
clearProviderCooldown(providerKey) {
|
|
875
|
+
if (!providerKey) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
if (this.providerCooldowns.delete(providerKey)) {
|
|
879
|
+
this.persistHealthSnapshot();
|
|
880
|
+
}
|
|
787
881
|
}
|
|
788
882
|
isProviderCoolingDown(providerKey) {
|
|
789
883
|
if (!providerKey) {
|
|
@@ -799,4 +893,88 @@ export class VirtualRouterEngine {
|
|
|
799
893
|
}
|
|
800
894
|
return true;
|
|
801
895
|
}
|
|
896
|
+
restoreHealthFromStore() {
|
|
897
|
+
if (!this.healthStore || typeof this.healthStore.loadInitialSnapshot !== 'function') {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
let snapshot = null;
|
|
901
|
+
try {
|
|
902
|
+
snapshot = this.healthStore.loadInitialSnapshot();
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
snapshot = null;
|
|
906
|
+
}
|
|
907
|
+
if (!snapshot) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const now = Date.now();
|
|
911
|
+
const providerKeys = new Set();
|
|
912
|
+
for (const pools of Object.values(this.routing)) {
|
|
913
|
+
for (const pool of pools) {
|
|
914
|
+
for (const key of pool.targets) {
|
|
915
|
+
if (typeof key === 'string' && key) {
|
|
916
|
+
providerKeys.add(key);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const byKey = new Map();
|
|
922
|
+
for (const entry of snapshot.cooldowns || []) {
|
|
923
|
+
if (!entry || !entry.providerKey) {
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
if (!providerKeys.has(entry.providerKey)) {
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (!Number.isFinite(entry.cooldownExpiresAt) || entry.cooldownExpiresAt <= now) {
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
byKey.set(entry.providerKey, entry);
|
|
933
|
+
this.providerCooldowns.set(entry.providerKey, entry.cooldownExpiresAt);
|
|
934
|
+
}
|
|
935
|
+
for (const state of snapshot.providers || []) {
|
|
936
|
+
if (!state || !state.providerKey) {
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
if (!providerKeys.has(state.providerKey)) {
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (state.cooldownExpiresAt && state.cooldownExpiresAt > now) {
|
|
943
|
+
const ttl = state.cooldownExpiresAt - now;
|
|
944
|
+
if (ttl > 0) {
|
|
945
|
+
this.healthManager.tripProvider(state.providerKey, state.reason, ttl);
|
|
946
|
+
if (!byKey.has(state.providerKey)) {
|
|
947
|
+
this.providerCooldowns.set(state.providerKey, state.cooldownExpiresAt);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
buildHealthSnapshot() {
|
|
954
|
+
const providers = this.healthManager.getSnapshot();
|
|
955
|
+
const cooldowns = [];
|
|
956
|
+
const now = Date.now();
|
|
957
|
+
for (const [providerKey, expiry] of this.providerCooldowns.entries()) {
|
|
958
|
+
if (!expiry || expiry <= now) {
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
cooldowns.push({
|
|
962
|
+
providerKey,
|
|
963
|
+
cooldownExpiresAt: expiry
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
return { providers, cooldowns };
|
|
967
|
+
}
|
|
968
|
+
persistHealthSnapshot() {
|
|
969
|
+
if (!this.healthStore || typeof this.healthStore.persistSnapshot !== 'function') {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
const snapshot = this.buildHealthSnapshot();
|
|
974
|
+
this.healthStore.persistSnapshot(snapshot);
|
|
975
|
+
}
|
|
976
|
+
catch {
|
|
977
|
+
// 持久化失败不影响路由主流程
|
|
978
|
+
}
|
|
979
|
+
}
|
|
802
980
|
}
|
|
@@ -31,6 +31,8 @@ export interface RoutingInstructionState {
|
|
|
31
31
|
stopMessageText?: string;
|
|
32
32
|
stopMessageMaxRepeats?: number;
|
|
33
33
|
stopMessageUsed?: number;
|
|
34
|
+
stopMessageUpdatedAt?: number;
|
|
35
|
+
stopMessageLastUsedAt?: number;
|
|
34
36
|
}
|
|
35
37
|
export declare function parseRoutingInstructions(messages: StandardizedMessage[]): RoutingInstruction[];
|
|
36
38
|
export declare function applyRoutingInstructions(instructions: RoutingInstruction[], currentState: RoutingInstructionState): RoutingInstructionState;
|
|
@@ -275,7 +275,9 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
275
275
|
disabledModels: new Map(Array.from(currentState.disabledModels.entries()).map(([k, v]) => [k, new Set(v)])),
|
|
276
276
|
stopMessageText: currentState.stopMessageText,
|
|
277
277
|
stopMessageMaxRepeats: currentState.stopMessageMaxRepeats,
|
|
278
|
-
stopMessageUsed: currentState.stopMessageUsed
|
|
278
|
+
stopMessageUsed: currentState.stopMessageUsed,
|
|
279
|
+
stopMessageUpdatedAt: currentState.stopMessageUpdatedAt,
|
|
280
|
+
stopMessageLastUsedAt: currentState.stopMessageLastUsedAt
|
|
279
281
|
};
|
|
280
282
|
let allowReset = false;
|
|
281
283
|
let disableReset = false;
|
|
@@ -399,6 +401,8 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
399
401
|
newState.stopMessageText = text;
|
|
400
402
|
newState.stopMessageMaxRepeats = maxRepeats;
|
|
401
403
|
newState.stopMessageUsed = 0;
|
|
404
|
+
newState.stopMessageUpdatedAt = Date.now();
|
|
405
|
+
newState.stopMessageLastUsedAt = undefined;
|
|
402
406
|
}
|
|
403
407
|
break;
|
|
404
408
|
}
|
|
@@ -406,6 +410,8 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
406
410
|
newState.stopMessageText = undefined;
|
|
407
411
|
newState.stopMessageMaxRepeats = undefined;
|
|
408
412
|
newState.stopMessageUsed = undefined;
|
|
413
|
+
newState.stopMessageUpdatedAt = undefined;
|
|
414
|
+
newState.stopMessageLastUsedAt = undefined;
|
|
409
415
|
break;
|
|
410
416
|
}
|
|
411
417
|
}
|
|
@@ -455,6 +461,12 @@ export function serializeRoutingInstructionState(state) {
|
|
|
455
461
|
: {}),
|
|
456
462
|
...(typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)
|
|
457
463
|
? { stopMessageUsed: state.stopMessageUsed }
|
|
464
|
+
: {}),
|
|
465
|
+
...(typeof state.stopMessageUpdatedAt === 'number' && Number.isFinite(state.stopMessageUpdatedAt)
|
|
466
|
+
? { stopMessageUpdatedAt: state.stopMessageUpdatedAt }
|
|
467
|
+
: {}),
|
|
468
|
+
...(typeof state.stopMessageLastUsedAt === 'number' && Number.isFinite(state.stopMessageLastUsedAt)
|
|
469
|
+
? { stopMessageLastUsedAt: state.stopMessageLastUsedAt }
|
|
458
470
|
: {})
|
|
459
471
|
};
|
|
460
472
|
}
|
|
@@ -505,5 +517,11 @@ export function deserializeRoutingInstructionState(data) {
|
|
|
505
517
|
if (typeof data.stopMessageUsed === 'number' && Number.isFinite(data.stopMessageUsed)) {
|
|
506
518
|
state.stopMessageUsed = Math.max(0, Math.floor(data.stopMessageUsed));
|
|
507
519
|
}
|
|
520
|
+
if (typeof data.stopMessageUpdatedAt === 'number' && Number.isFinite(data.stopMessageUpdatedAt)) {
|
|
521
|
+
state.stopMessageUpdatedAt = data.stopMessageUpdatedAt;
|
|
522
|
+
}
|
|
523
|
+
if (typeof data.stopMessageLastUsedAt === 'number' && Number.isFinite(data.stopMessageLastUsedAt)) {
|
|
524
|
+
state.stopMessageLastUsedAt = data.stopMessageLastUsedAt;
|
|
525
|
+
}
|
|
508
526
|
return state;
|
|
509
527
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StandardizedMessage, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
|
|
2
|
-
export type ToolCategory = 'read' | 'write' | 'search' | 'other';
|
|
2
|
+
export type ToolCategory = 'read' | 'write' | 'search' | 'websearch' | 'other';
|
|
3
3
|
export type ToolClassification = {
|
|
4
4
|
category: ToolCategory;
|
|
5
5
|
name: string;
|
|
@@ -10,4 +10,5 @@ export declare function detectCodingTool(request: StandardizedRequest): boolean;
|
|
|
10
10
|
export declare function detectWebTool(request: StandardizedRequest): boolean;
|
|
11
11
|
export declare function extractMeaningfulDeclaredToolNames(tools: StandardizedRequest['tools'] | undefined): string[];
|
|
12
12
|
export declare function detectLastAssistantToolCategory(messages: StandardizedMessage[]): ToolClassification | undefined;
|
|
13
|
+
export declare function classifyToolCallForReport(call: StandardizedMessage['tool_calls'][number]): ToolClassification | undefined;
|
|
13
14
|
export declare function canonicalizeToolName(rawName: string): string;
|