@jsonstudio/llms 0.6.802 → 0.6.938
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/bridge/routecodex-adapter.d.ts +74 -0
- package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
- package/dist/config-unified/unified-config.d.ts +26 -0
- package/dist/conversion/codec-registry.d.ts +10 -0
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
- package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
- package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
- package/dist/conversion/config/config-manager.d.ts +212 -0
- package/dist/conversion/hub/config/types.d.ts +26 -0
- package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
- package/dist/conversion/hub/core/hub-context.d.ts +21 -0
- package/dist/conversion/hub/core/index.d.ts +3 -0
- package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
- package/dist/conversion/hub/hub-feature.d.ts +1 -0
- package/dist/conversion/hub/node-support.d.ts +19 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +71 -14
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
- package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
- package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
- package/dist/conversion/hub/policy/policy-engine.js +176 -0
- package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
- package/dist/conversion/hub/policy/protocol-spec.js +105 -0
- package/dist/conversion/hub/process/chat-process.d.ts +32 -0
- package/dist/conversion/hub/registry.d.ts +28 -0
- package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
- package/dist/conversion/hub/response/provider-response.js +31 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +87 -1
- package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
- package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
- package/dist/conversion/hub/types/errors.d.ts +5 -0
- package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
- package/dist/conversion/hub/types/index.d.ts +6 -0
- package/dist/conversion/hub/types/json.d.ts +9 -0
- package/dist/conversion/hub/types/node.d.ts +31 -0
- package/dist/conversion/responses/responses-openai-bridge.js +263 -10
- package/dist/conversion/schema-validator.d.ts +7 -0
- package/dist/conversion/shared/args-mapping.d.ts +18 -0
- package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/gemini-tool-utils.js +61 -0
- package/dist/conversion/shared/jsonish.d.ts +3 -0
- package/dist/conversion/shared/mcp-injection.d.ts +2 -0
- package/dist/conversion/shared/media.d.ts +1 -0
- package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
- package/dist/conversion/shared/payload-budget.d.ts +13 -0
- package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
- package/dist/conversion/shared/responses-request-adapter.js +1 -430
- package/dist/conversion/shared/snapshot-hooks.js +58 -3
- package/dist/conversion/shared/tool-governor.js +8 -2
- package/dist/conversion/shared/tool-harvester.d.ts +31 -0
- package/dist/conversion/shared/tool-mapping.js +10 -29
- package/dist/conversion/types.d.ts +33 -0
- package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
- package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
- package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
- package/dist/filters/engine.d.ts +16 -0
- package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
- package/dist/filters/special/response-finish-invariants.d.ts +11 -0
- package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
- package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
- package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
- package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
- package/dist/filters/special/tool-post-constraints.d.ts +31 -0
- package/dist/filters/types.d.ts +68 -0
- package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
- package/dist/filters/utils/snapshot-writer.d.ts +10 -0
- package/dist/guidance/index.d.ts +3 -0
- package/dist/guidance/index.js +78 -83
- package/dist/http/sse-response.d.ts +22 -0
- package/dist/router/virtual-router/bootstrap.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap.js +49 -5
- package/dist/router/virtual-router/classifier.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection.js +98 -11
- package/dist/router/virtual-router/engine.js +177 -31
- package/dist/router/virtual-router/error-center.d.ts +10 -0
- package/dist/router/virtual-router/features.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
- package/dist/router/virtual-router/routing-instructions.js +120 -30
- package/dist/router/virtual-router/types.d.ts +11 -0
- package/dist/servertool/engine.js +189 -16
- package/dist/servertool/handlers/apply-patch-guard.js +269 -0
- package/dist/servertool/handlers/exec-command-guard.js +558 -0
- package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
- package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
- package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
- package/dist/servertool/handlers/followup-request-builder.js +122 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
- package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
- package/dist/servertool/handlers/stop-message-auto.js +237 -75
- package/dist/servertool/handlers/vision.js +15 -27
- package/dist/servertool/handlers/web-search.js +17 -43
- package/dist/servertool/server-side-tools.d.ts +3 -0
- package/dist/servertool/server-side-tools.js +3 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
- package/dist/sse/shared/chat-serializer.d.ts +4 -0
- package/dist/sse/shared/constants.d.ts +272 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
- package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/index.d.ts +2 -1
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
- package/dist/sse/shared/serializers/types.d.ts +51 -0
- package/dist/sse/shared/utils.d.ts +254 -0
- package/dist/sse/shared/writer.d.ts +2 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/types/chat-types.d.ts +1 -1
- package/dist/sse/types/responses-types.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
- package/dist/tools/apply-patch/execution-capturer.js +158 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
- package/dist/tools/apply-patch/regression-capturer.js +5 -4
- package/dist/tools/apply-patch/structured.js +109 -13
- package/dist/tools/apply-patch/validator.js +112 -18
- package/dist/tools/tool-registry.d.ts +8 -0
- package/dist/tools/tool-registry.js +2 -1
- package/package.json +4 -4
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
- package/dist/conversion/config/compat-profiles.json +0 -38
- package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
- package/dist/conversion/hub/pipeline/context-limit.js +0 -55
- package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
- package/dist/conversion/hub/response/server-side-tools.js +0 -383
- package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
- package/dist/conversion/shared/bridge-conversation-store.js +0 -279
- package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
- package/dist/conversion/shared/bridge-request-adapter.js +0 -430
- package/dist/conversion/shared/responses-id-utils.js +0 -42
- package/dist/conversion/shared/responses-instructions.js +0 -113
- package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
- package/dist/conversion/shared/responses-message-utils.js +0 -206
- package/dist/conversion/shared/responses-metadata.js +0 -1
- package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
- package/dist/conversion/shared/responses-output-utils.js +0 -108
- package/dist/conversion/shared/responses-types.d.ts +0 -33
- package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
- package/dist/conversion/shared/tool-normalizers.js +0 -84
- package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
- package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
- package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
- package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
- package/dist/servertool/flow-types.d.ts +0 -40
- package/dist/servertool/flow-types.js +0 -1
- package/dist/servertool/orchestration-types.d.ts +0 -33
- package/dist/servertool/orchestration-types.js +0 -1
- package/dist/servertool/vision-tool.d.ts +0 -2
- package/dist/servertool/vision-tool.js +0 -185
- package/dist/tools/patch-args-normalizer.d.ts +0 -15
- package/dist/tools/patch-args-normalizer.js +0 -472
- package/dist/utils/toon.d.ts +0 -4
- package/dist/utils/toon.js +0 -75
- /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
- /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import { isJsonObject } from '../types/json.js';
|
|
2
|
+
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
3
3
|
import { VirtualRouterEngine } from '../../../router/virtual-router/engine.js';
|
|
4
4
|
import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
|
|
5
5
|
import { defaultSseCodecRegistry } from '../../../sse/index.js';
|
|
@@ -25,6 +25,7 @@ import { runReqOutboundStage3Compat } from './stages/req_outbound/req_outbound_s
|
|
|
25
25
|
import { extractSessionIdentifiersFromMetadata } from './session-identifiers.js';
|
|
26
26
|
import { computeRequestTokens } from '../../../router/virtual-router/token-estimator.js';
|
|
27
27
|
import { isCompactionRequest } from '../../shared/compaction-detect.js';
|
|
28
|
+
import { applyHubProviderOutboundPolicy, recordHubPolicyObservation, setHubPolicyRuntimePolicy } from '../policy/policy-engine.js';
|
|
28
29
|
export class HubPipeline {
|
|
29
30
|
routerEngine;
|
|
30
31
|
config;
|
|
@@ -37,6 +38,7 @@ export class HubPipeline {
|
|
|
37
38
|
quotaView: config.quotaView
|
|
38
39
|
});
|
|
39
40
|
this.routerEngine.initialize(config.virtualRouter);
|
|
41
|
+
setHubPolicyRuntimePolicy(config.policy);
|
|
40
42
|
try {
|
|
41
43
|
this.unsubscribeProviderErrors = providerErrorCenter.subscribe((event) => {
|
|
42
44
|
try {
|
|
@@ -97,6 +99,19 @@ export class HubPipeline {
|
|
|
97
99
|
const formatAdapter = hooks.createFormatAdapter();
|
|
98
100
|
const semanticMapper = hooks.createSemanticMapper();
|
|
99
101
|
const rawRequest = this.asJsonObject(normalized.payload);
|
|
102
|
+
// Preserve the client-provided raw tool definitions (schema included) for response-side validation
|
|
103
|
+
// and protocol-correct outbound tool-call formatting. This must reflect the inbound request shape,
|
|
104
|
+
// not the governed/augmented tools.
|
|
105
|
+
try {
|
|
106
|
+
const toolsRaw = Array.isArray(rawRequest?.tools) ? rawRequest.tools : null;
|
|
107
|
+
if (toolsRaw && toolsRaw.length > 0) {
|
|
108
|
+
normalized.metadata = normalized.metadata || {};
|
|
109
|
+
normalized.metadata.clientToolsRaw = jsonClone(toolsRaw);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// best-effort: do not block request handling due to tool snapshot failures
|
|
114
|
+
}
|
|
100
115
|
if (isCompactionRequest(rawRequest)) {
|
|
101
116
|
normalized.metadata = normalized.metadata || {};
|
|
102
117
|
normalized.metadata.compactionRequest = true;
|
|
@@ -104,6 +119,14 @@ export class HubPipeline {
|
|
|
104
119
|
const inboundAdapterContext = this.buildAdapterContext(normalized);
|
|
105
120
|
const inboundRecorder = this.maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint);
|
|
106
121
|
const inboundStart = Date.now();
|
|
122
|
+
// Phase 0: observe client inbound payload violations (best-effort; no rewrites).
|
|
123
|
+
recordHubPolicyObservation({
|
|
124
|
+
providerProtocol: this.resolveClientProtocol(normalized.entryEndpoint),
|
|
125
|
+
payload: rawRequest,
|
|
126
|
+
phase: 'client_inbound',
|
|
127
|
+
stageRecorder: inboundRecorder,
|
|
128
|
+
requestId: normalized.id
|
|
129
|
+
});
|
|
107
130
|
const formatEnvelope = await runReqInboundStage1FormatParse({
|
|
108
131
|
rawRequest,
|
|
109
132
|
adapterContext: inboundAdapterContext,
|
|
@@ -139,20 +162,26 @@ export class HubPipeline {
|
|
|
139
162
|
}
|
|
140
163
|
}
|
|
141
164
|
});
|
|
165
|
+
// 将 VirtualRouter 层的 servertool 相关配置注入到 metadata,保证响应侧
|
|
166
|
+
// servertool(第三跳 reenter)也能访问到相同配置,即使当前 route 标记为 passthrough。
|
|
167
|
+
const metaBase = {
|
|
168
|
+
...(normalized.metadata ?? {})
|
|
169
|
+
};
|
|
170
|
+
const webSearchConfig = this.config.virtualRouter?.webSearch;
|
|
171
|
+
if (webSearchConfig) {
|
|
172
|
+
metaBase.webSearch = webSearchConfig;
|
|
173
|
+
}
|
|
174
|
+
const execCommandGuard = this.config.virtualRouter?.execCommandGuard;
|
|
175
|
+
if (execCommandGuard) {
|
|
176
|
+
metaBase.execCommandGuard = execCommandGuard;
|
|
177
|
+
}
|
|
178
|
+
normalized.metadata = metaBase;
|
|
142
179
|
let processedRequest;
|
|
143
180
|
if (normalized.processMode !== 'passthrough') {
|
|
144
|
-
const processMetadata = {
|
|
145
|
-
...(normalized.metadata ?? {})
|
|
146
|
-
};
|
|
147
|
-
const webSearchConfig = this.config.virtualRouter?.webSearch;
|
|
148
|
-
if (webSearchConfig) {
|
|
149
|
-
processMetadata.webSearch = webSearchConfig;
|
|
150
|
-
}
|
|
151
|
-
normalized.metadata = processMetadata;
|
|
152
181
|
const processResult = await runReqProcessStage1ToolGovernance({
|
|
153
182
|
request: standardizedRequest,
|
|
154
183
|
rawPayload: rawRequest,
|
|
155
|
-
metadata:
|
|
184
|
+
metadata: metaBase,
|
|
156
185
|
entryEndpoint: normalized.entryEndpoint,
|
|
157
186
|
requestId: normalized.id,
|
|
158
187
|
stageRecorder: inboundRecorder
|
|
@@ -273,7 +302,20 @@ export class HubPipeline {
|
|
|
273
302
|
adapterContext: outboundAdapterContext,
|
|
274
303
|
stageRecorder: outboundRecorder
|
|
275
304
|
});
|
|
276
|
-
providerPayload =
|
|
305
|
+
providerPayload = applyHubProviderOutboundPolicy({
|
|
306
|
+
policy: this.config.policy,
|
|
307
|
+
providerProtocol: outboundProtocol,
|
|
308
|
+
payload: formattedPayload,
|
|
309
|
+
stageRecorder: outboundRecorder,
|
|
310
|
+
requestId: normalized.id
|
|
311
|
+
});
|
|
312
|
+
recordHubPolicyObservation({
|
|
313
|
+
policy: this.config.policy,
|
|
314
|
+
providerProtocol: outboundProtocol,
|
|
315
|
+
payload: providerPayload,
|
|
316
|
+
stageRecorder: outboundRecorder,
|
|
317
|
+
requestId: normalized.id
|
|
318
|
+
});
|
|
277
319
|
const outboundEnd = Date.now();
|
|
278
320
|
nodeResults.push({
|
|
279
321
|
id: 'req_outbound',
|
|
@@ -305,11 +347,13 @@ export class HubPipeline {
|
|
|
305
347
|
// 对于 capturedChatRequest,我们只需要一个“可读快照”,不会在后续流程中
|
|
306
348
|
// 对其做就地修改,因此可以直接使用浅拷贝结构,避免序列化失败导致整段
|
|
307
349
|
// 逻辑失效。
|
|
350
|
+
// Deep-clone a JSON-safe snapshot for servertool followups.
|
|
351
|
+
// Only capture the canonical Chat payload fields (model/messages/tools/parameters) to keep it serializable.
|
|
308
352
|
const capturedChatRequest = {
|
|
309
353
|
model: workingRequest.model,
|
|
310
|
-
messages:
|
|
311
|
-
tools: workingRequest.tools,
|
|
312
|
-
parameters: workingRequest.parameters
|
|
354
|
+
messages: jsonClone(workingRequest.messages),
|
|
355
|
+
tools: workingRequest.tools ? jsonClone(workingRequest.tools) : workingRequest.tools,
|
|
356
|
+
parameters: workingRequest.parameters ? jsonClone(workingRequest.parameters) : workingRequest.parameters
|
|
313
357
|
};
|
|
314
358
|
const metadata = {
|
|
315
359
|
...normalized.metadata,
|
|
@@ -335,6 +379,14 @@ export class HubPipeline {
|
|
|
335
379
|
nodeResults
|
|
336
380
|
};
|
|
337
381
|
}
|
|
382
|
+
resolveClientProtocol(entryEndpoint) {
|
|
383
|
+
const lowered = String(entryEndpoint || '').toLowerCase();
|
|
384
|
+
if (lowered.includes('/v1/responses'))
|
|
385
|
+
return 'openai-responses';
|
|
386
|
+
if (lowered.includes('/v1/messages'))
|
|
387
|
+
return 'anthropic-messages';
|
|
388
|
+
return 'openai-chat';
|
|
389
|
+
}
|
|
338
390
|
async execute(request) {
|
|
339
391
|
const normalized = await this.normalizeRequest(request);
|
|
340
392
|
const hooks = this.resolveProtocolHooks(normalized.providerProtocol);
|
|
@@ -457,6 +509,11 @@ export class HubPipeline {
|
|
|
457
509
|
adapterContext.serverToolFollowup = metadata
|
|
458
510
|
.serverToolFollowup;
|
|
459
511
|
}
|
|
512
|
+
// Preserve raw client tools (schemas) for response-side validation/formatting.
|
|
513
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'clientToolsRaw')) {
|
|
514
|
+
adapterContext.clientToolsRaw = metadata
|
|
515
|
+
.clientToolsRaw;
|
|
516
|
+
}
|
|
460
517
|
const sessionId = typeof metadata.sessionId === 'string'
|
|
461
518
|
? metadata.sessionId.trim()
|
|
462
519
|
: '';
|
package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runHubChatProcess } from '../../../../process/chat-process.js';
|
|
2
2
|
import { recordStage } from '../../../stages/utils.js';
|
|
3
|
+
import { captureApplyPatchExecutionFailuresFromProcessedRequest } from '../../../../../../tools/apply-patch/execution-capturer.js';
|
|
3
4
|
export async function runReqProcessStage1ToolGovernance(options) {
|
|
4
5
|
const result = await runHubChatProcess({
|
|
5
6
|
request: options.request,
|
|
@@ -10,6 +11,9 @@ export async function runReqProcessStage1ToolGovernance(options) {
|
|
|
10
11
|
});
|
|
11
12
|
if (result.processedRequest) {
|
|
12
13
|
recordStage(options.stageRecorder, 'req_process_stage1_tool_governance', result.processedRequest);
|
|
14
|
+
// Best-effort: capture apply_patch execution failures reported by tool role messages.
|
|
15
|
+
// This is for errorsamples collection only and must not affect runtime behavior.
|
|
16
|
+
captureApplyPatchExecutionFailuresFromProcessedRequest(result.processedRequest);
|
|
13
17
|
}
|
|
14
18
|
return result;
|
|
15
19
|
}
|
package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js
CHANGED
|
@@ -12,8 +12,10 @@ export function runRespOutboundStage1ClientRemap(options) {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
15
|
+
const toolsRaw = resolveClientToolsRawFromContext(options.adapterContext);
|
|
15
16
|
clientPayload = buildResponsesPayloadFromChat(options.payload, {
|
|
16
|
-
requestId: options.requestId
|
|
17
|
+
requestId: options.requestId,
|
|
18
|
+
...(toolsRaw ? { toolsRaw } : {})
|
|
17
19
|
});
|
|
18
20
|
}
|
|
19
21
|
recordStage(options.stageRecorder, 'resp_outbound_stage1_client_remap', clientPayload);
|
|
@@ -41,3 +43,23 @@ function resolveAliasMapFromContext(adapterContext) {
|
|
|
41
43
|
}
|
|
42
44
|
return Object.keys(map).length ? map : undefined;
|
|
43
45
|
}
|
|
46
|
+
function resolveClientToolsRawFromContext(adapterContext) {
|
|
47
|
+
if (!adapterContext) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const candidate = adapterContext.clientToolsRaw;
|
|
51
|
+
if (!candidate || !Array.isArray(candidate)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const filtered = [];
|
|
55
|
+
for (const entry of candidate) {
|
|
56
|
+
if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
|
|
57
|
+
const type = entry.type;
|
|
58
|
+
if (typeof type !== 'string' || !type.trim()) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
filtered.push(entry);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return filtered.length ? filtered : undefined;
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ChatEnvelope, AdapterContext } from '../types/chat-envelope.js';
|
|
2
|
+
import type { FormatAdapter, SemanticMapper, StageRecorder } from '../format-adapters/index.js';
|
|
3
|
+
import type { JsonObject } from '../types/json.js';
|
|
4
|
+
export type InboundStage = 'format_parse' | 'semantic_map_to_chat' | 'inbound_passthrough';
|
|
5
|
+
export interface InboundPassthroughConfig {
|
|
6
|
+
mode: 'chat';
|
|
7
|
+
factory: (raw: JsonObject, ctx: AdapterContext) => Promise<ChatEnvelope> | ChatEnvelope;
|
|
8
|
+
}
|
|
9
|
+
export interface InboundPlan {
|
|
10
|
+
protocol?: string;
|
|
11
|
+
stages: InboundStage[];
|
|
12
|
+
formatAdapter?: Pick<FormatAdapter, 'parseRequest'>;
|
|
13
|
+
semanticMapper?: Pick<SemanticMapper, 'toChat'>;
|
|
14
|
+
passthrough?: InboundPassthroughConfig;
|
|
15
|
+
}
|
|
16
|
+
export interface InboundPipelineOptions {
|
|
17
|
+
rawRequest: JsonObject;
|
|
18
|
+
context: AdapterContext;
|
|
19
|
+
plan: InboundPlan;
|
|
20
|
+
stageRecorder?: StageRecorder;
|
|
21
|
+
}
|
|
22
|
+
export declare function runInboundPipeline(options: InboundPipelineOptions): Promise<ChatEnvelope>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ChatEnvelope, AdapterContext } from '../types/chat-envelope.js';
|
|
2
|
+
import type { FormatAdapter, SemanticMapper, StageRecorder } from '../format-adapters/index.js';
|
|
3
|
+
import type { JsonObject } from '../types/json.js';
|
|
4
|
+
export type OutboundStage = 'semantic_map_from_chat' | 'format_build' | 'outbound_passthrough';
|
|
5
|
+
export interface OutboundPassthroughConfig {
|
|
6
|
+
mode: 'protocol';
|
|
7
|
+
factory: (chat: ChatEnvelope, ctx: AdapterContext) => Promise<JsonObject> | JsonObject;
|
|
8
|
+
}
|
|
9
|
+
export interface OutboundPlan {
|
|
10
|
+
protocol?: string;
|
|
11
|
+
stages: OutboundStage[];
|
|
12
|
+
formatAdapter?: Pick<FormatAdapter, 'buildResponse'>;
|
|
13
|
+
semanticMapper?: Pick<SemanticMapper, 'fromChat'>;
|
|
14
|
+
passthrough?: OutboundPassthroughConfig;
|
|
15
|
+
}
|
|
16
|
+
export interface OutboundPipelineOptions {
|
|
17
|
+
chat: ChatEnvelope;
|
|
18
|
+
context: AdapterContext;
|
|
19
|
+
plan: OutboundPlan;
|
|
20
|
+
stageRecorder?: StageRecorder;
|
|
21
|
+
}
|
|
22
|
+
export declare function runOutboundPipeline(options: OutboundPipelineOptions): Promise<JsonObject>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { JsonObject } from '../types/json.js';
|
|
2
|
+
import type { StageRecorder } from '../format-adapters/index.js';
|
|
3
|
+
export type HubPolicyMode = 'off' | 'observe' | 'enforce';
|
|
4
|
+
export interface HubPolicyConfig {
|
|
5
|
+
mode?: HubPolicyMode;
|
|
6
|
+
/**
|
|
7
|
+
* Optional: sampling rate in [0, 1]. When provided, observation is best-effort
|
|
8
|
+
* and may skip recording for some requests.
|
|
9
|
+
*/
|
|
10
|
+
sampleRate?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface PolicyObservation {
|
|
13
|
+
phase: 'client_inbound' | 'provider_outbound' | 'provider_inbound' | 'client_outbound';
|
|
14
|
+
providerProtocol: string;
|
|
15
|
+
violations: Array<{
|
|
16
|
+
code: 'unexpected_wrapper' | 'unexpected_field';
|
|
17
|
+
path: string;
|
|
18
|
+
detail?: string;
|
|
19
|
+
}>;
|
|
20
|
+
summary: {
|
|
21
|
+
totalViolations: number;
|
|
22
|
+
unexpectedFieldCount: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface ProviderOutboundPolicyApplyResult {
|
|
26
|
+
payload: JsonObject;
|
|
27
|
+
changed: boolean;
|
|
28
|
+
removedTopLevelKeys: string[];
|
|
29
|
+
flattenedWrappers: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function setHubPolicyRuntimePolicy(policy?: HubPolicyConfig): void;
|
|
32
|
+
export declare function recordHubPolicyObservation(options: {
|
|
33
|
+
policy?: HubPolicyConfig;
|
|
34
|
+
phase?: PolicyObservation['phase'];
|
|
35
|
+
providerProtocol: string;
|
|
36
|
+
payload: JsonObject;
|
|
37
|
+
stageRecorder?: StageRecorder;
|
|
38
|
+
requestId?: string;
|
|
39
|
+
}): void;
|
|
40
|
+
export declare function applyHubProviderOutboundPolicy(options: {
|
|
41
|
+
policy?: HubPolicyConfig;
|
|
42
|
+
providerProtocol: string;
|
|
43
|
+
payload: JsonObject;
|
|
44
|
+
stageRecorder?: StageRecorder;
|
|
45
|
+
requestId?: string;
|
|
46
|
+
}): JsonObject;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { resolveHubProtocolSpec } from './protocol-spec.js';
|
|
2
|
+
let hubPolicyRuntime = undefined;
|
|
3
|
+
export function setHubPolicyRuntimePolicy(policy) {
|
|
4
|
+
hubPolicyRuntime = policy;
|
|
5
|
+
}
|
|
6
|
+
function resolveEffectivePolicy(policy) {
|
|
7
|
+
return policy ?? hubPolicyRuntime;
|
|
8
|
+
}
|
|
9
|
+
function shouldSample(rate) {
|
|
10
|
+
if (rate === undefined)
|
|
11
|
+
return true;
|
|
12
|
+
if (!Number.isFinite(rate))
|
|
13
|
+
return true;
|
|
14
|
+
if (rate <= 0)
|
|
15
|
+
return false;
|
|
16
|
+
if (rate >= 1)
|
|
17
|
+
return true;
|
|
18
|
+
return Math.random() < rate;
|
|
19
|
+
}
|
|
20
|
+
function isJsonRecord(value) {
|
|
21
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function applyProviderOutboundPolicy(providerProtocol, payload) {
|
|
24
|
+
const removedTopLevelKeys = [];
|
|
25
|
+
const flattenedWrappers = [];
|
|
26
|
+
const spec = resolveHubProtocolSpec(providerProtocol);
|
|
27
|
+
if (!spec.providerOutbound.enforceEnabled) {
|
|
28
|
+
return {
|
|
29
|
+
payload,
|
|
30
|
+
changed: false,
|
|
31
|
+
removedTopLevelKeys,
|
|
32
|
+
flattenedWrappers
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
let out = payload;
|
|
36
|
+
const ensureOutClone = () => {
|
|
37
|
+
if (out === payload) {
|
|
38
|
+
out = { ...payload };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
// Reserved/private keys must never be sent upstream.
|
|
42
|
+
for (const key of Object.keys(payload)) {
|
|
43
|
+
if (spec.providerOutbound.reservedKeyPrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
44
|
+
ensureOutClone();
|
|
45
|
+
delete out[key];
|
|
46
|
+
removedTopLevelKeys.push(key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Flatten accidental wrappers that have caused upstream 400s.
|
|
50
|
+
for (const rule of spec.providerOutbound.flattenWrappers) {
|
|
51
|
+
const wrapperKey = rule.wrapperKey;
|
|
52
|
+
if (!wrapperKey || typeof wrapperKey !== 'string') {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!isJsonRecord(out[wrapperKey])) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
ensureOutClone();
|
|
59
|
+
const inner = { ...out[wrapperKey] };
|
|
60
|
+
const alias = rule.aliasKeys || {};
|
|
61
|
+
for (const [from, to] of Object.entries(alias)) {
|
|
62
|
+
if (inner[to] === undefined && inner[from] !== undefined) {
|
|
63
|
+
inner[to] = inner[from];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const allow = Array.isArray(rule.allowKeys) ? new Set(rule.allowKeys) : null;
|
|
67
|
+
const onlyIfMissing = rule.onlyIfTargetMissing !== false;
|
|
68
|
+
for (const [key, value] of Object.entries(inner)) {
|
|
69
|
+
if (allow && !allow.has(key)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!onlyIfMissing || out[key] === undefined) {
|
|
73
|
+
out[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
delete out[wrapperKey];
|
|
77
|
+
flattenedWrappers.push(wrapperKey);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
payload: out,
|
|
81
|
+
changed: out !== payload || removedTopLevelKeys.length > 0 || flattenedWrappers.length > 0,
|
|
82
|
+
removedTopLevelKeys,
|
|
83
|
+
flattenedWrappers
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function observeProviderOutboundPayload(providerProtocol, payload) {
|
|
87
|
+
const violations = [];
|
|
88
|
+
// V0 (observe-only): detect known "layout anti-patterns" and reserved keys.
|
|
89
|
+
// Do NOT modify payload here.
|
|
90
|
+
const spec = resolveHubProtocolSpec(providerProtocol);
|
|
91
|
+
for (const rule of spec.providerOutbound.forbidWrappers) {
|
|
92
|
+
if (rule.code !== 'forbid_wrapper') {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (rule.path in payload && isJsonRecord(payload[rule.path])) {
|
|
96
|
+
violations.push({
|
|
97
|
+
code: 'unexpected_wrapper',
|
|
98
|
+
path: rule.path,
|
|
99
|
+
detail: rule.detail
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Always record unknown private wrapper keys (best-effort, conservative).
|
|
104
|
+
for (const key of Object.keys(payload)) {
|
|
105
|
+
if (spec.providerOutbound.reservedKeyPrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
106
|
+
violations.push({
|
|
107
|
+
code: 'unexpected_field',
|
|
108
|
+
path: key
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const unexpectedFieldCount = violations.filter((v) => v.code === 'unexpected_field').length;
|
|
113
|
+
return {
|
|
114
|
+
phase: 'provider_outbound',
|
|
115
|
+
providerProtocol,
|
|
116
|
+
violations,
|
|
117
|
+
summary: {
|
|
118
|
+
totalViolations: violations.length,
|
|
119
|
+
unexpectedFieldCount
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export function recordHubPolicyObservation(options) {
|
|
124
|
+
if (!options.stageRecorder) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const effectivePolicy = resolveEffectivePolicy(options.policy);
|
|
128
|
+
const mode = effectivePolicy?.mode ?? 'off';
|
|
129
|
+
// Keep observing in enforce mode (best-effort) so operators can monitor
|
|
130
|
+
// violations while gradually turning enforcement on.
|
|
131
|
+
if (mode !== 'observe' && mode !== 'enforce') {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!shouldSample(effectivePolicy?.sampleRate)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const phase = options.phase ?? 'provider_outbound';
|
|
139
|
+
const observation = observeProviderOutboundPayload(options.providerProtocol, options.payload);
|
|
140
|
+
observation.phase = phase;
|
|
141
|
+
if (observation.summary.totalViolations <= 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const stage = `hub_policy.observe.${phase}`;
|
|
145
|
+
options.stageRecorder.record(stage, {
|
|
146
|
+
requestId: options.requestId,
|
|
147
|
+
...observation
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// observe-only must never break the pipeline
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function applyHubProviderOutboundPolicy(options) {
|
|
155
|
+
const effectivePolicy = resolveEffectivePolicy(options.policy);
|
|
156
|
+
const mode = effectivePolicy?.mode ?? 'off';
|
|
157
|
+
if (mode !== 'enforce') {
|
|
158
|
+
return options.payload;
|
|
159
|
+
}
|
|
160
|
+
const result = applyProviderOutboundPolicy(options.providerProtocol, options.payload);
|
|
161
|
+
if (!result.changed) {
|
|
162
|
+
return options.payload;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
options.stageRecorder?.record('hub_policy.enforce.provider_outbound', {
|
|
166
|
+
requestId: options.requestId,
|
|
167
|
+
providerProtocol: options.providerProtocol,
|
|
168
|
+
removedTopLevelKeys: result.removedTopLevelKeys,
|
|
169
|
+
flattenedWrappers: result.flattenedWrappers
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// policy enforcement recording must not break the pipeline
|
|
174
|
+
}
|
|
175
|
+
return result.payload;
|
|
176
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type HubProviderProtocol = 'openai-chat' | 'openai-responses' | 'anthropic-messages' | 'gemini-chat';
|
|
2
|
+
export interface ProviderOutboundLayoutRule {
|
|
3
|
+
code: 'forbid_wrapper';
|
|
4
|
+
path: string;
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ProviderOutboundWrapperFlattenRule {
|
|
8
|
+
wrapperKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* When provided, only these keys from the wrapper are merged into top-level.
|
|
11
|
+
* When omitted, all keys are eligible.
|
|
12
|
+
*/
|
|
13
|
+
allowKeys?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Key remap inside wrapper prior to merging (e.g. max_tokens -> max_output_tokens).
|
|
16
|
+
*/
|
|
17
|
+
aliasKeys?: Record<string, string>;
|
|
18
|
+
/**
|
|
19
|
+
* Merge strategy: only write to target when top-level key is undefined.
|
|
20
|
+
*/
|
|
21
|
+
onlyIfTargetMissing?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ProviderOutboundPolicySpec {
|
|
24
|
+
/**
|
|
25
|
+
* Whether provider outbound enforcement is enabled for this protocol.
|
|
26
|
+
* Keep this false for protocols not yet migrated, to avoid behavior changes.
|
|
27
|
+
*/
|
|
28
|
+
enforceEnabled: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Reserved/private key prefixes that must not be sent upstream.
|
|
31
|
+
* (Enforced only when enforceEnabled=true.)
|
|
32
|
+
*/
|
|
33
|
+
reservedKeyPrefixes: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Wrappers that should not exist in provider outbound payload.
|
|
36
|
+
* observe: treat as violations; enforce: flatten/remove when configured.
|
|
37
|
+
*/
|
|
38
|
+
forbidWrappers: ProviderOutboundLayoutRule[];
|
|
39
|
+
/**
|
|
40
|
+
* Best-effort fix for known wrapper anti-patterns by flattening into top-level.
|
|
41
|
+
* (Applied only when enforceEnabled=true.)
|
|
42
|
+
*/
|
|
43
|
+
flattenWrappers: ProviderOutboundWrapperFlattenRule[];
|
|
44
|
+
}
|
|
45
|
+
export interface ProtocolSpec {
|
|
46
|
+
id: HubProviderProtocol;
|
|
47
|
+
providerOutbound: ProviderOutboundPolicySpec;
|
|
48
|
+
}
|
|
49
|
+
export declare const HUB_PROTOCOL_SPECS: Record<HubProviderProtocol, ProtocolSpec>;
|
|
50
|
+
export declare function resolveHubProtocolSpec(protocol: string): ProtocolSpec;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const RESPONSES_SPEC = {
|
|
2
|
+
id: 'openai-responses',
|
|
3
|
+
providerOutbound: {
|
|
4
|
+
enforceEnabled: true,
|
|
5
|
+
forbidWrappers: [
|
|
6
|
+
{
|
|
7
|
+
code: 'forbid_wrapper',
|
|
8
|
+
path: 'parameters',
|
|
9
|
+
detail: 'Responses provider payload must not contain a top-level parameters wrapper (expects flattened fields).'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
code: 'forbid_wrapper',
|
|
13
|
+
path: 'request',
|
|
14
|
+
detail: 'Responses provider payload must not contain a nested request wrapper.'
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
reservedKeyPrefixes: ['__', '_'],
|
|
18
|
+
flattenWrappers: [
|
|
19
|
+
{
|
|
20
|
+
wrapperKey: 'request',
|
|
21
|
+
onlyIfTargetMissing: true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
wrapperKey: 'parameters',
|
|
25
|
+
onlyIfTargetMissing: true,
|
|
26
|
+
aliasKeys: {
|
|
27
|
+
max_tokens: 'max_output_tokens'
|
|
28
|
+
},
|
|
29
|
+
allowKeys: [
|
|
30
|
+
'temperature',
|
|
31
|
+
'top_p',
|
|
32
|
+
'max_output_tokens',
|
|
33
|
+
'seed',
|
|
34
|
+
'logit_bias',
|
|
35
|
+
'user',
|
|
36
|
+
'parallel_tool_calls',
|
|
37
|
+
'tool_choice',
|
|
38
|
+
'response_format',
|
|
39
|
+
'stream',
|
|
40
|
+
'stop',
|
|
41
|
+
'stop_sequences',
|
|
42
|
+
'modalities',
|
|
43
|
+
'top_k'
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const DEFAULT_SPEC = {
|
|
50
|
+
id: 'openai-chat',
|
|
51
|
+
providerOutbound: {
|
|
52
|
+
enforceEnabled: false,
|
|
53
|
+
forbidWrappers: [
|
|
54
|
+
{
|
|
55
|
+
code: 'forbid_wrapper',
|
|
56
|
+
path: 'parameters',
|
|
57
|
+
detail: 'OpenAI Chat provider payload must not contain a top-level parameters wrapper (expects flattened fields).'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: 'forbid_wrapper',
|
|
61
|
+
path: 'request',
|
|
62
|
+
detail: 'OpenAI Chat provider payload must not contain a nested request wrapper.'
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
reservedKeyPrefixes: ['__', '_'],
|
|
66
|
+
flattenWrappers: []
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
export const HUB_PROTOCOL_SPECS = {
|
|
70
|
+
'openai-chat': DEFAULT_SPEC,
|
|
71
|
+
'openai-responses': RESPONSES_SPEC,
|
|
72
|
+
'anthropic-messages': {
|
|
73
|
+
id: 'anthropic-messages',
|
|
74
|
+
providerOutbound: {
|
|
75
|
+
enforceEnabled: false,
|
|
76
|
+
forbidWrappers: [
|
|
77
|
+
{
|
|
78
|
+
code: 'forbid_wrapper',
|
|
79
|
+
path: 'parameters',
|
|
80
|
+
detail: 'Anthropic Messages provider payload must not contain a top-level parameters wrapper.'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
code: 'forbid_wrapper',
|
|
84
|
+
path: 'request',
|
|
85
|
+
detail: 'Anthropic Messages provider payload must not contain a nested request wrapper.'
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
reservedKeyPrefixes: ['__', '_'],
|
|
89
|
+
flattenWrappers: []
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
'gemini-chat': {
|
|
93
|
+
id: 'gemini-chat',
|
|
94
|
+
providerOutbound: {
|
|
95
|
+
enforceEnabled: false,
|
|
96
|
+
forbidWrappers: [],
|
|
97
|
+
reservedKeyPrefixes: ['__', '_'],
|
|
98
|
+
flattenWrappers: []
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
export function resolveHubProtocolSpec(protocol) {
|
|
103
|
+
const normalized = (protocol || '').trim().toLowerCase();
|
|
104
|
+
return HUB_PROTOCOL_SPECS[normalized] ?? DEFAULT_SPEC;
|
|
105
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ProcessedRequest, StandardizedRequest } from '../types/standardized.js';
|
|
2
|
+
import type { JsonObject } from '../types/json.js';
|
|
3
|
+
export interface HubChatProcessOptions {
|
|
4
|
+
request: StandardizedRequest;
|
|
5
|
+
requestId: string;
|
|
6
|
+
entryEndpoint: string;
|
|
7
|
+
rawPayload: Record<string, unknown>;
|
|
8
|
+
metadata: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface HubChatProcessResult {
|
|
11
|
+
processedRequest?: ProcessedRequest;
|
|
12
|
+
nodeResult: HubProcessNodeResult;
|
|
13
|
+
}
|
|
14
|
+
export interface HubProcessNodeResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
metadata: {
|
|
17
|
+
node: string;
|
|
18
|
+
executionTime: number;
|
|
19
|
+
startTime: number;
|
|
20
|
+
endTime: number;
|
|
21
|
+
dataProcessed?: {
|
|
22
|
+
messages: number;
|
|
23
|
+
tools: number;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
error?: {
|
|
27
|
+
code?: string;
|
|
28
|
+
message: string;
|
|
29
|
+
details?: JsonObject;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare function runHubChatProcess(options: HubChatProcessOptions): Promise<HubChatProcessResult>;
|