@jsonstudio/llms 0.4.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/codecs/anthropic-openai-codec.js +28 -2
- package/dist/conversion/codecs/gemini-openai-codec.js +23 -0
- package/dist/conversion/codecs/responses-openai-codec.js +8 -1
- package/dist/conversion/hub/node-support.js +14 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +66 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +284 -193
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.d.ts +11 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +16 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +17 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-factories.d.ts +5 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/context-factories.js +17 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.d.ts +19 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +269 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.d.ts +18 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +141 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +11 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +29 -0
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.d.ts +16 -0
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +15 -0
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.d.ts +17 -0
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +18 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.d.ts +17 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +63 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.d.ts +11 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +12 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +13 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +43 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.d.ts +17 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +22 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.d.ts +16 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +19 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.d.ts +17 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +19 -0
- package/dist/conversion/hub/pipeline/stages/utils.d.ts +2 -0
- package/dist/conversion/hub/pipeline/stages/utils.js +11 -0
- package/dist/conversion/hub/pipeline/target-utils.d.ts +5 -0
- package/dist/conversion/hub/pipeline/target-utils.js +87 -0
- package/dist/conversion/hub/process/chat-process.js +11 -11
- package/dist/conversion/hub/response/provider-response.js +69 -122
- package/dist/conversion/hub/response/response-mappers.d.ts +19 -0
- package/dist/conversion/hub/response/response-mappers.js +22 -2
- package/dist/conversion/hub/response/response-runtime.d.ts +8 -0
- package/dist/conversion/hub/response/response-runtime.js +239 -6
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +8 -0
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +119 -59
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +74 -13
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +0 -9
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +16 -13
- package/dist/conversion/hub/snapshot-recorder.d.ts +13 -0
- package/dist/conversion/hub/snapshot-recorder.js +90 -50
- package/dist/conversion/hub/standardized-bridge.js +44 -30
- package/dist/conversion/hub/types/chat-envelope.d.ts +68 -0
- package/dist/conversion/hub/types/standardized.d.ts +97 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +29 -2
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +68 -1
- package/dist/conversion/responses/responses-openai-bridge.d.ts +6 -1
- package/dist/conversion/responses/responses-openai-bridge.js +132 -6
- package/dist/conversion/shared/anthropic-message-utils.d.ts +9 -1
- package/dist/conversion/shared/anthropic-message-utils.js +334 -14
- package/dist/conversion/shared/bridge-actions.js +267 -40
- package/dist/conversion/shared/bridge-message-utils.js +54 -8
- package/dist/conversion/shared/bridge-policies.js +29 -4
- package/dist/conversion/shared/chat-envelope-validator.d.ts +8 -0
- package/dist/conversion/shared/chat-envelope-validator.js +128 -0
- package/dist/conversion/shared/chat-request-filters.js +108 -25
- package/dist/conversion/shared/mcp-injection.js +41 -20
- package/dist/conversion/shared/openai-finalizer.d.ts +11 -0
- package/dist/conversion/shared/openai-finalizer.js +73 -0
- package/dist/conversion/shared/openai-message-normalize.js +32 -31
- package/dist/conversion/shared/reasoning-normalizer.d.ts +1 -0
- package/dist/conversion/shared/reasoning-normalizer.js +50 -18
- package/dist/conversion/shared/responses-output-builder.d.ts +1 -1
- package/dist/conversion/shared/responses-output-builder.js +76 -25
- package/dist/conversion/shared/responses-reasoning-registry.d.ts +8 -0
- package/dist/conversion/shared/responses-reasoning-registry.js +61 -0
- package/dist/conversion/shared/responses-response-utils.js +32 -2
- package/dist/conversion/shared/responses-tool-utils.js +28 -2
- package/dist/conversion/shared/snapshot-hooks.d.ts +9 -0
- package/dist/conversion/shared/snapshot-hooks.js +60 -6
- package/dist/conversion/shared/snapshot-utils.d.ts +16 -0
- package/dist/conversion/shared/snapshot-utils.js +84 -0
- package/dist/conversion/shared/tool-filter-pipeline.js +45 -5
- package/dist/conversion/shared/tool-governor.js +5 -0
- package/dist/conversion/shared/tool-mapping.js +13 -2
- package/dist/filters/special/request-tool-choice-policy.js +3 -1
- package/dist/filters/special/request-tool-list-filter.d.ts +11 -0
- package/dist/filters/special/request-tool-list-filter.js +20 -7
- package/dist/sse/shared/responses-output-normalizer.js +5 -4
- package/package.json +1 -1
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import { runHubInboundConversion, runHubOutboundConversion } from '../node-support.js';
|
|
3
2
|
import { VirtualRouterEngine } from '../../../router/virtual-router/engine.js';
|
|
4
3
|
import { defaultSseCodecRegistry } from '../../../sse/index.js';
|
|
5
|
-
import {
|
|
4
|
+
import { ResponsesFormatAdapter } from '../format-adapters/responses-format-adapter.js';
|
|
5
|
+
import { ResponsesSemanticMapper } from '../semantic-mappers/responses-mapper.js';
|
|
6
|
+
import { AnthropicFormatAdapter } from '../format-adapters/anthropic-format-adapter.js';
|
|
7
|
+
import { AnthropicSemanticMapper } from '../semantic-mappers/anthropic-mapper.js';
|
|
8
|
+
import { GeminiFormatAdapter } from '../format-adapters/gemini-format-adapter.js';
|
|
9
|
+
import { GeminiSemanticMapper } from '../semantic-mappers/gemini-mapper.js';
|
|
10
|
+
import { ChatFormatAdapter } from '../format-adapters/chat-format-adapter.js';
|
|
11
|
+
import { ChatSemanticMapper } from '../semantic-mappers/chat-mapper.js';
|
|
12
|
+
import { createSnapshotRecorder } from '../snapshot-recorder.js';
|
|
13
|
+
import { runReqInboundStage1FormatParse } from './stages/req_inbound/req_inbound_stage1_format_parse/index.js';
|
|
14
|
+
import { runReqInboundStage2SemanticMap } from './stages/req_inbound/req_inbound_stage2_semantic_map/index.js';
|
|
15
|
+
import { runChatContextCapture, captureResponsesContextSnapshot } from './stages/req_inbound/req_inbound_stage3_context_capture/index.js';
|
|
16
|
+
import { createResponsesContextCapture, createNoopContextCapture } from './stages/req_inbound/req_inbound_stage3_context_capture/context-factories.js';
|
|
17
|
+
import { runReqProcessStage1ToolGovernance } from './stages/req_process/req_process_stage1_tool_governance/index.js';
|
|
18
|
+
import { runReqProcessStage2RouteSelect } from './stages/req_process/req_process_stage2_route_select/index.js';
|
|
19
|
+
import { runReqOutboundStage1SemanticMap } from './stages/req_outbound/req_outbound_stage1_semantic_map/index.js';
|
|
20
|
+
import { runReqOutboundStage2FormatBuild } from './stages/req_outbound/req_outbound_stage2_format_build/index.js';
|
|
6
21
|
export class HubPipeline {
|
|
7
22
|
routerEngine;
|
|
8
23
|
config;
|
|
@@ -18,23 +33,128 @@ export class HubPipeline {
|
|
|
18
33
|
this.config.virtualRouter = nextConfig;
|
|
19
34
|
this.routerEngine.initialize(nextConfig);
|
|
20
35
|
}
|
|
21
|
-
async
|
|
22
|
-
const
|
|
36
|
+
async executeRequestStagePipeline(normalized, hooks) {
|
|
37
|
+
const formatAdapter = hooks.createFormatAdapter();
|
|
38
|
+
const semanticMapper = hooks.createSemanticMapper();
|
|
39
|
+
const inboundAdapterContext = this.buildAdapterContext(normalized);
|
|
40
|
+
const inboundRecorder = this.maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint);
|
|
41
|
+
const rawRequest = this.asJsonObject(normalized.payload);
|
|
42
|
+
const inboundStart = Date.now();
|
|
43
|
+
const formatEnvelope = await runReqInboundStage1FormatParse({
|
|
44
|
+
rawRequest,
|
|
45
|
+
adapterContext: inboundAdapterContext,
|
|
46
|
+
formatAdapter,
|
|
47
|
+
stageRecorder: inboundRecorder
|
|
48
|
+
});
|
|
49
|
+
const inboundStage2 = await runReqInboundStage2SemanticMap({
|
|
50
|
+
adapterContext: inboundAdapterContext,
|
|
51
|
+
formatEnvelope,
|
|
52
|
+
semanticMapper,
|
|
53
|
+
stageRecorder: inboundRecorder
|
|
54
|
+
});
|
|
55
|
+
this.captureAnthropicAliasMap(normalized, inboundAdapterContext, inboundStage2.chatEnvelope);
|
|
56
|
+
const contextSnapshot = await hooks.captureContext({
|
|
57
|
+
rawRequest,
|
|
58
|
+
adapterContext: inboundAdapterContext,
|
|
59
|
+
stageRecorder: inboundRecorder
|
|
60
|
+
});
|
|
61
|
+
const standardizedRequest = inboundStage2.standardizedRequest;
|
|
62
|
+
const inboundEnd = Date.now();
|
|
23
63
|
const nodeResults = [];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
64
|
+
nodeResults.push({
|
|
65
|
+
id: 'req_inbound',
|
|
66
|
+
success: true,
|
|
67
|
+
metadata: {
|
|
68
|
+
node: 'req_inbound',
|
|
69
|
+
executionTime: inboundEnd - inboundStart,
|
|
70
|
+
startTime: inboundStart,
|
|
71
|
+
endTime: inboundEnd,
|
|
72
|
+
dataProcessed: {
|
|
73
|
+
messages: standardizedRequest.messages.length,
|
|
74
|
+
tools: standardizedRequest.tools?.length ?? 0
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
let processedRequest;
|
|
79
|
+
if (normalized.processMode !== 'passthrough') {
|
|
80
|
+
const processResult = await runReqProcessStage1ToolGovernance({
|
|
81
|
+
request: standardizedRequest,
|
|
82
|
+
rawPayload: rawRequest,
|
|
83
|
+
metadata: normalized.metadata,
|
|
84
|
+
entryEndpoint: normalized.entryEndpoint,
|
|
85
|
+
requestId: normalized.id,
|
|
86
|
+
stageRecorder: inboundRecorder
|
|
87
|
+
});
|
|
88
|
+
processedRequest = processResult.processedRequest;
|
|
89
|
+
if (processResult.nodeResult) {
|
|
90
|
+
nodeResults.push(this.convertProcessNodeResult('req_process_stage1_tool_governance', processResult.nodeResult));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const workingRequest = processedRequest ?? standardizedRequest;
|
|
94
|
+
const metadataInput = {
|
|
95
|
+
requestId: normalized.id,
|
|
96
|
+
entryEndpoint: normalized.entryEndpoint,
|
|
97
|
+
processMode: normalized.processMode,
|
|
98
|
+
stream: normalized.stream,
|
|
99
|
+
direction: normalized.direction,
|
|
100
|
+
providerProtocol: normalized.providerProtocol,
|
|
101
|
+
routeHint: normalized.routeHint,
|
|
102
|
+
stage: normalized.stage
|
|
103
|
+
};
|
|
104
|
+
const routing = runReqProcessStage2RouteSelect({
|
|
105
|
+
routerEngine: this.routerEngine,
|
|
106
|
+
request: workingRequest,
|
|
107
|
+
metadataInput,
|
|
108
|
+
normalizedMetadata: normalized.metadata,
|
|
109
|
+
stageRecorder: inboundRecorder
|
|
110
|
+
});
|
|
111
|
+
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
112
|
+
const outboundProtocol = outboundAdapterContext.providerProtocol;
|
|
113
|
+
const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
|
|
114
|
+
const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
|
|
115
|
+
if (!outboundHooks) {
|
|
116
|
+
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
29
117
|
}
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
118
|
+
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
|
|
119
|
+
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
|
|
120
|
+
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
121
|
+
const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
|
|
122
|
+
const outboundEndpoint = resolveEndpointForProviderProtocol(outboundAdapterContext.providerProtocol);
|
|
123
|
+
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, outboundEndpoint);
|
|
124
|
+
const outboundStart = Date.now();
|
|
125
|
+
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
126
|
+
request: workingRequest,
|
|
127
|
+
adapterContext: outboundAdapterContext,
|
|
128
|
+
semanticMapper: outboundSemanticMapper,
|
|
129
|
+
contextSnapshot: outboundContextSnapshot,
|
|
130
|
+
contextMetadataKey: outboundContextMetadataKey,
|
|
131
|
+
stageRecorder: outboundRecorder
|
|
132
|
+
});
|
|
133
|
+
const providerPayload = await runReqOutboundStage2FormatBuild({
|
|
134
|
+
formatEnvelope: outboundStage1.formatEnvelope,
|
|
135
|
+
adapterContext: outboundAdapterContext,
|
|
136
|
+
formatAdapter: outboundFormatAdapter,
|
|
137
|
+
stageRecorder: outboundRecorder
|
|
138
|
+
});
|
|
139
|
+
const outboundEnd = Date.now();
|
|
140
|
+
nodeResults.push({
|
|
141
|
+
id: 'req_outbound',
|
|
142
|
+
success: true,
|
|
143
|
+
metadata: {
|
|
144
|
+
node: 'req_outbound',
|
|
145
|
+
executionTime: outboundEnd - outboundStart,
|
|
146
|
+
startTime: outboundStart,
|
|
147
|
+
endTime: outboundEnd,
|
|
148
|
+
dataProcessed: {
|
|
149
|
+
messages: workingRequest.messages.length,
|
|
150
|
+
tools: workingRequest.tools?.length ?? 0
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
34
154
|
const metadata = {
|
|
35
155
|
...normalized.metadata,
|
|
36
156
|
entryEndpoint: normalized.entryEndpoint,
|
|
37
|
-
providerProtocol:
|
|
157
|
+
providerProtocol: outboundProtocol,
|
|
38
158
|
stream: normalized.stream,
|
|
39
159
|
processMode: normalized.processMode,
|
|
40
160
|
routeHint: normalized.routeHint,
|
|
@@ -42,8 +162,8 @@ export class HubPipeline {
|
|
|
42
162
|
};
|
|
43
163
|
return {
|
|
44
164
|
requestId: normalized.id,
|
|
45
|
-
providerPayload
|
|
46
|
-
standardizedRequest
|
|
165
|
+
providerPayload,
|
|
166
|
+
standardizedRequest,
|
|
47
167
|
processedRequest,
|
|
48
168
|
routingDecision: routing.decision,
|
|
49
169
|
routingDiagnostics: routing.diagnostics,
|
|
@@ -52,181 +172,125 @@ export class HubPipeline {
|
|
|
52
172
|
nodeResults
|
|
53
173
|
};
|
|
54
174
|
}
|
|
55
|
-
async
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
nodeContext: hubContext,
|
|
61
|
-
nodeId: 'hub-inbound',
|
|
62
|
-
inputFormat: normalized.entryEndpoint,
|
|
63
|
-
outputFormat: 'standardized-request',
|
|
64
|
-
startTime: Date.now()
|
|
65
|
-
});
|
|
66
|
-
const standardized = inbound.data?.standardizedRequest;
|
|
67
|
-
if (!standardized) {
|
|
68
|
-
throw new Error('Hub inbound pipeline did not return a standardized request');
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
standardizedRequest: standardized,
|
|
72
|
-
nodeResult: this.convertHubNodeResult('hub-inbound', inbound)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
async runProcessStage(normalized, standardizedRequest) {
|
|
76
|
-
if (normalized.processMode === 'passthrough') {
|
|
77
|
-
return {};
|
|
175
|
+
async execute(request) {
|
|
176
|
+
const normalized = await this.normalizeRequest(request);
|
|
177
|
+
const hooks = this.resolveProtocolHooks(normalized.providerProtocol);
|
|
178
|
+
if (!hooks) {
|
|
179
|
+
throw new Error(`Unsupported provider protocol for hub pipeline: ${normalized.providerProtocol}`);
|
|
78
180
|
}
|
|
79
|
-
|
|
80
|
-
request: standardizedRequest,
|
|
81
|
-
requestId: normalized.id,
|
|
82
|
-
entryEndpoint: normalized.entryEndpoint,
|
|
83
|
-
rawPayload: normalized.payload,
|
|
84
|
-
metadata: normalized.metadata
|
|
85
|
-
});
|
|
86
|
-
return {
|
|
87
|
-
processedRequest: processResult.processedRequest,
|
|
88
|
-
nodeResult: this.convertProcessNodeResult('hub-chat-process', processResult.nodeResult)
|
|
89
|
-
};
|
|
181
|
+
return await this.executeRequestStagePipeline(normalized, hooks);
|
|
90
182
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
requestId: normalized.id,
|
|
94
|
-
entryEndpoint: normalized.entryEndpoint,
|
|
95
|
-
processMode: normalized.processMode,
|
|
96
|
-
stream: normalized.stream,
|
|
97
|
-
direction: normalized.direction,
|
|
98
|
-
providerProtocol: normalized.providerProtocol,
|
|
99
|
-
routeHint: normalized.routeHint,
|
|
100
|
-
stage: normalized.stage
|
|
101
|
-
};
|
|
102
|
-
const previousModel = typeof request?.model === 'string'
|
|
103
|
-
? request.model
|
|
104
|
-
: undefined;
|
|
105
|
-
const result = this.routerEngine.route(request, metadata);
|
|
106
|
-
this.applyTargetMetadata(normalized.metadata, result.target, result.decision.routeName, previousModel);
|
|
107
|
-
this.applyTargetToSubject(request, result.target, previousModel);
|
|
108
|
-
return {
|
|
109
|
-
target: result.target,
|
|
110
|
-
decision: result.decision,
|
|
111
|
-
diagnostics: result.diagnostics
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
applyTargetMetadata(metadata, target, routeName, originalModel) {
|
|
115
|
-
if (!metadata || typeof metadata !== 'object')
|
|
183
|
+
captureAnthropicAliasMap(normalized, adapterContext, chatEnvelope) {
|
|
184
|
+
if (!this.shouldCaptureAnthropicAlias(normalized.entryEndpoint)) {
|
|
116
185
|
return;
|
|
117
|
-
metadata.routeName = routeName;
|
|
118
|
-
metadata.pipelineId = target.providerKey;
|
|
119
|
-
metadata.target = target;
|
|
120
|
-
metadata.providerKey = target.providerKey;
|
|
121
|
-
metadata.providerType = target.providerType;
|
|
122
|
-
metadata.modelId = target.modelId;
|
|
123
|
-
metadata.processMode = target.processMode || 'chat';
|
|
124
|
-
if (target.responsesConfig?.toolCallIdStyle) {
|
|
125
|
-
metadata.toolCallIdStyle = target.responsesConfig.toolCallIdStyle;
|
|
126
186
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
metadata.originalModelId = trimmed;
|
|
131
|
-
}
|
|
132
|
-
if (typeof metadata.clientModelId !== 'string' || !metadata.clientModelId) {
|
|
133
|
-
metadata.clientModelId = trimmed;
|
|
134
|
-
}
|
|
187
|
+
const aliasMap = this.resolveAliasMapFromSources(adapterContext, chatEnvelope);
|
|
188
|
+
if (!aliasMap) {
|
|
189
|
+
return;
|
|
135
190
|
}
|
|
136
|
-
|
|
137
|
-
|
|
191
|
+
normalized.metadata = normalized.metadata || {};
|
|
192
|
+
if (!normalized.metadata.anthropicToolNameMap) {
|
|
193
|
+
normalized.metadata.anthropicToolNameMap = aliasMap;
|
|
138
194
|
}
|
|
139
195
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
196
|
+
shouldCaptureAnthropicAlias(endpoint) {
|
|
197
|
+
return typeof endpoint === 'string' && endpoint.toLowerCase().includes('/v1/messages');
|
|
198
|
+
}
|
|
199
|
+
resolveAliasMapFromSources(adapterContext, chatEnvelope) {
|
|
200
|
+
const fromContext = coerceAliasMap(adapterContext.anthropicToolNameMap);
|
|
201
|
+
if (fromContext) {
|
|
202
|
+
return fromContext;
|
|
203
|
+
}
|
|
204
|
+
const metadataNode = chatEnvelope.metadata;
|
|
205
|
+
const direct = metadataNode ? coerceAliasMap(metadataNode.anthropicToolNameMap) : undefined;
|
|
206
|
+
if (direct) {
|
|
207
|
+
return direct;
|
|
152
208
|
}
|
|
153
|
-
|
|
154
|
-
|
|
209
|
+
const contextNode = metadataNode && metadataNode.context && typeof metadataNode.context === 'object'
|
|
210
|
+
? metadataNode.context
|
|
211
|
+
: undefined;
|
|
212
|
+
return coerceAliasMap(contextNode?.anthropicToolNameMap);
|
|
213
|
+
}
|
|
214
|
+
resolveProtocolHooks(protocol) {
|
|
215
|
+
switch (protocol) {
|
|
216
|
+
case 'openai-chat':
|
|
217
|
+
return {
|
|
218
|
+
createFormatAdapter: () => new ChatFormatAdapter(),
|
|
219
|
+
createSemanticMapper: () => new ChatSemanticMapper(),
|
|
220
|
+
captureContext: (options) => runChatContextCapture(options),
|
|
221
|
+
contextMetadataKey: 'chatContext'
|
|
222
|
+
};
|
|
223
|
+
case 'openai-responses':
|
|
224
|
+
return {
|
|
225
|
+
createFormatAdapter: () => new ResponsesFormatAdapter(),
|
|
226
|
+
createSemanticMapper: () => new ResponsesSemanticMapper(),
|
|
227
|
+
captureContext: createResponsesContextCapture(captureResponsesContextSnapshot),
|
|
228
|
+
contextMetadataKey: 'responsesContext'
|
|
229
|
+
};
|
|
230
|
+
case 'anthropic-messages':
|
|
231
|
+
return {
|
|
232
|
+
createFormatAdapter: () => new AnthropicFormatAdapter(),
|
|
233
|
+
createSemanticMapper: () => new AnthropicSemanticMapper(),
|
|
234
|
+
captureContext: (options) => runChatContextCapture(options),
|
|
235
|
+
contextMetadataKey: 'anthropicContext'
|
|
236
|
+
};
|
|
237
|
+
case 'gemini-chat':
|
|
238
|
+
return {
|
|
239
|
+
createFormatAdapter: () => new GeminiFormatAdapter(),
|
|
240
|
+
createSemanticMapper: () => new GeminiSemanticMapper(),
|
|
241
|
+
captureContext: createNoopContextCapture('gemini-chat')
|
|
242
|
+
};
|
|
243
|
+
default:
|
|
244
|
+
return undefined;
|
|
155
245
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
246
|
+
}
|
|
247
|
+
buildAdapterContext(normalized, target) {
|
|
248
|
+
const metadata = normalized.metadata || {};
|
|
249
|
+
const providerProtocol = target?.outboundProfile || normalized.providerProtocol;
|
|
250
|
+
const providerId = (target?.providerKey || metadata.providerKey);
|
|
251
|
+
const routeId = metadata.routeName;
|
|
252
|
+
const profileId = (target?.providerKey || metadata.pipelineId);
|
|
253
|
+
const streamingHint = normalized.stream === true ? 'force' : normalized.stream === false ? 'disable' : 'auto';
|
|
254
|
+
const toolCallIdStyle = normalizeToolCallIdStyleCandidate(metadata.toolCallIdStyle);
|
|
255
|
+
const adapterContext = {
|
|
256
|
+
requestId: normalized.id,
|
|
257
|
+
entryEndpoint: normalized.entryEndpoint || '/v1/chat/completions',
|
|
258
|
+
providerProtocol,
|
|
259
|
+
providerId,
|
|
260
|
+
routeId,
|
|
261
|
+
profileId,
|
|
262
|
+
streamingHint,
|
|
263
|
+
toolCallIdStyle
|
|
161
264
|
};
|
|
162
|
-
if (
|
|
163
|
-
|
|
265
|
+
if (typeof metadata.originalModelId === 'string') {
|
|
266
|
+
adapterContext.originalModelId = metadata.originalModelId;
|
|
164
267
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const trimmed = originalModel.trim();
|
|
168
|
-
if (typeof subjectMeta.originalModelId !== 'string' || !subjectMeta.originalModelId) {
|
|
169
|
-
subjectMeta.originalModelId = trimmed;
|
|
170
|
-
}
|
|
171
|
-
if (typeof subjectMeta.clientModelId !== 'string' || !subjectMeta.clientModelId) {
|
|
172
|
-
subjectMeta.clientModelId = trimmed;
|
|
173
|
-
}
|
|
268
|
+
if (typeof metadata.clientModelId === 'string') {
|
|
269
|
+
adapterContext.clientModelId = metadata.clientModelId;
|
|
174
270
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
extractModelFromTarget(target) {
|
|
178
|
-
if (!target || typeof target.providerKey !== 'string') {
|
|
179
|
-
return null;
|
|
271
|
+
if (typeof metadata.assignedModelId === 'string') {
|
|
272
|
+
adapterContext.modelId = metadata.assignedModelId;
|
|
180
273
|
}
|
|
181
|
-
|
|
182
|
-
|
|
274
|
+
return adapterContext;
|
|
275
|
+
}
|
|
276
|
+
maybeCreateStageRecorder(context, endpoint) {
|
|
277
|
+
const flag = (process.env.ROUTECODEX_HUB_SNAPSHOTS || '').trim();
|
|
278
|
+
if (flag === '0') {
|
|
279
|
+
return undefined;
|
|
183
280
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const candidate = target.providerKey.slice(prefix.length).trim();
|
|
188
|
-
if (candidate.length) {
|
|
189
|
-
return candidate;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
281
|
+
const effectiveEndpoint = endpoint || context.entryEndpoint || '/v1/chat/completions';
|
|
282
|
+
try {
|
|
283
|
+
return createSnapshotRecorder(context, effectiveEndpoint);
|
|
192
284
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return null;
|
|
285
|
+
catch {
|
|
286
|
+
return undefined;
|
|
196
287
|
}
|
|
197
|
-
const fallback = target.providerKey.slice(firstDot + 1).trim();
|
|
198
|
-
return fallback.length ? fallback : null;
|
|
199
288
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
...normalized.metadata,
|
|
206
|
-
target
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
const start = Date.now();
|
|
210
|
-
const payload = await runHubOutboundConversion({
|
|
211
|
-
protocol: outboundProtocol,
|
|
212
|
-
request,
|
|
213
|
-
nodeContext: outboundContext
|
|
214
|
-
});
|
|
215
|
-
const nodeResult = {
|
|
216
|
-
id: 'hub-outbound',
|
|
217
|
-
success: true,
|
|
218
|
-
metadata: {
|
|
219
|
-
node: 'hub-outbound',
|
|
220
|
-
executionTime: Date.now() - start,
|
|
221
|
-
startTime: start,
|
|
222
|
-
endTime: Date.now(),
|
|
223
|
-
dataProcessed: {
|
|
224
|
-
messages: request.messages.length,
|
|
225
|
-
tools: request.tools?.length ?? 0
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
return { payload, nodeResult };
|
|
289
|
+
asJsonObject(value) {
|
|
290
|
+
if (!value || typeof value !== 'object') {
|
|
291
|
+
throw new Error('Responses pipeline requires JSON object payload');
|
|
292
|
+
}
|
|
293
|
+
return value;
|
|
230
294
|
}
|
|
231
295
|
async normalizeRequest(request) {
|
|
232
296
|
if (!request || typeof request !== 'object') {
|
|
@@ -281,25 +345,6 @@ export class HubPipeline {
|
|
|
281
345
|
routeHint
|
|
282
346
|
};
|
|
283
347
|
}
|
|
284
|
-
createHubNodeContext(normalized) {
|
|
285
|
-
return {
|
|
286
|
-
request: {
|
|
287
|
-
id: normalized.id,
|
|
288
|
-
endpoint: normalized.entryEndpoint,
|
|
289
|
-
context: {
|
|
290
|
-
metadata: normalized.metadata
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
convertHubNodeResult(id, result) {
|
|
296
|
-
return {
|
|
297
|
-
id,
|
|
298
|
-
success: result.success,
|
|
299
|
-
metadata: result.metadata,
|
|
300
|
-
error: result.error
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
348
|
convertProcessNodeResult(id, result) {
|
|
304
349
|
return {
|
|
305
350
|
id,
|
|
@@ -383,6 +428,19 @@ export class HubPipeline {
|
|
|
383
428
|
return undefined;
|
|
384
429
|
}
|
|
385
430
|
}
|
|
431
|
+
function normalizeToolCallIdStyleCandidate(value) {
|
|
432
|
+
if (typeof value !== 'string') {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
const normalized = value.trim().toLowerCase();
|
|
436
|
+
if (normalized === 'fc') {
|
|
437
|
+
return 'fc';
|
|
438
|
+
}
|
|
439
|
+
if (normalized === 'preserve') {
|
|
440
|
+
return 'preserve';
|
|
441
|
+
}
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
386
444
|
function normalizeEndpoint(endpoint) {
|
|
387
445
|
if (!endpoint)
|
|
388
446
|
return '/v1/chat/completions';
|
|
@@ -416,6 +474,21 @@ const PROVIDER_PROTOCOL_ALIASES = {
|
|
|
416
474
|
'google-gemini': 'gemini-chat',
|
|
417
475
|
'gemini-chat': 'gemini-chat'
|
|
418
476
|
};
|
|
477
|
+
function resolveEndpointForProviderProtocol(protocol) {
|
|
478
|
+
if (!protocol) {
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
const value = protocol.toLowerCase();
|
|
482
|
+
if (value === 'openai-responses')
|
|
483
|
+
return '/v1/responses';
|
|
484
|
+
if (value === 'openai-chat')
|
|
485
|
+
return '/v1/chat/completions';
|
|
486
|
+
if (value === 'anthropic-messages' || value === 'anthropic')
|
|
487
|
+
return '/v1/messages';
|
|
488
|
+
if (value === 'gemini-chat' || value === 'gemini' || value === 'google-gemini')
|
|
489
|
+
return '/v1/responses';
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
419
492
|
function resolveSseProtocolFromMetadata(metadata) {
|
|
420
493
|
const candidate = metadata.sseProtocol ?? metadata.clientSseProtocol ?? metadata.routeSseProtocol;
|
|
421
494
|
if (typeof candidate !== 'string' || !candidate.trim()) {
|
|
@@ -423,3 +496,21 @@ function resolveSseProtocolFromMetadata(metadata) {
|
|
|
423
496
|
}
|
|
424
497
|
return resolveProviderProtocol(candidate);
|
|
425
498
|
}
|
|
499
|
+
function coerceAliasMap(candidate) {
|
|
500
|
+
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
const normalized = {};
|
|
504
|
+
for (const [key, value] of Object.entries(candidate)) {
|
|
505
|
+
if (typeof key !== 'string' || typeof value !== 'string') {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const trimmedKey = key.trim();
|
|
509
|
+
const trimmedValue = value.trim();
|
|
510
|
+
if (!trimmedKey.length || !trimmedValue.length) {
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
normalized[trimmedKey] = trimmedValue;
|
|
514
|
+
}
|
|
515
|
+
return Object.keys(normalized).length ? normalized : undefined;
|
|
516
|
+
}
|
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AdapterContext } from '../../../../types/chat-envelope.js';
|
|
2
|
+
import type { FormatEnvelope } from '../../../../types/format-envelope.js';
|
|
3
|
+
import type { JsonObject } from '../../../../types/json.js';
|
|
4
|
+
import type { FormatAdapter, StageRecorder } from '../../../../format-adapters/index.js';
|
|
5
|
+
export interface ReqInboundStage1FormatParseOptions {
|
|
6
|
+
rawRequest: JsonObject;
|
|
7
|
+
adapterContext: AdapterContext;
|
|
8
|
+
formatAdapter: Pick<FormatAdapter, 'parseRequest'>;
|
|
9
|
+
stageRecorder?: StageRecorder;
|
|
10
|
+
}
|
|
11
|
+
export declare function runReqInboundStage1FormatParse(options: ReqInboundStage1FormatParseOptions): Promise<FormatEnvelope<JsonObject>>;
|
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { recordStage } from '../../../stages/utils.js';
|
|
2
|
+
export async function runReqInboundStage1FormatParse(options) {
|
|
3
|
+
const envelope = (await options.formatAdapter.parseRequest(options.rawRequest, options.adapterContext));
|
|
4
|
+
recordStage(options.stageRecorder, 'req_inbound_stage1_format_parse', envelope);
|
|
5
|
+
return envelope;
|
|
6
|
+
}
|
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AdapterContext, ChatEnvelope } from '../../../../types/chat-envelope.js';
|
|
2
|
+
import type { FormatEnvelope } from '../../../../types/format-envelope.js';
|
|
3
|
+
import type { JsonObject } from '../../../../types/json.js';
|
|
4
|
+
import type { SemanticMapper, StageRecorder } from '../../../../format-adapters/index.js';
|
|
5
|
+
import type { StandardizedRequest } from '../../../../types/standardized.js';
|
|
6
|
+
export interface ReqInboundStage2SemanticMapOptions {
|
|
7
|
+
adapterContext: AdapterContext;
|
|
8
|
+
formatEnvelope: FormatEnvelope<JsonObject>;
|
|
9
|
+
semanticMapper: Pick<SemanticMapper, 'toChat'>;
|
|
10
|
+
stageRecorder?: StageRecorder;
|
|
11
|
+
}
|
|
12
|
+
export interface ReqInboundStage2SemanticMapResult {
|
|
13
|
+
chatEnvelope: ChatEnvelope;
|
|
14
|
+
standardizedRequest: StandardizedRequest;
|
|
15
|
+
}
|
|
16
|
+
export declare function runReqInboundStage2SemanticMap(options: ReqInboundStage2SemanticMapOptions): Promise<ReqInboundStage2SemanticMapResult>;
|
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { chatEnvelopeToStandardized } from '../../../../standardized-bridge.js';
|
|
2
|
+
import { validateChatEnvelope } from '../../../../../shared/chat-envelope-validator.js';
|
|
3
|
+
import { recordStage } from '../../../stages/utils.js';
|
|
4
|
+
export async function runReqInboundStage2SemanticMap(options) {
|
|
5
|
+
const chatEnvelope = await options.semanticMapper.toChat(options.formatEnvelope, options.adapterContext);
|
|
6
|
+
validateChatEnvelope(chatEnvelope, {
|
|
7
|
+
stage: 'req_inbound',
|
|
8
|
+
direction: 'request'
|
|
9
|
+
});
|
|
10
|
+
recordStage(options.stageRecorder, 'req_inbound_stage2_semantic_map', chatEnvelope);
|
|
11
|
+
const standardizedRequest = chatEnvelopeToStandardized(chatEnvelope, {
|
|
12
|
+
adapterContext: options.adapterContext,
|
|
13
|
+
endpoint: options.adapterContext.entryEndpoint,
|
|
14
|
+
requestId: options.adapterContext.requestId
|
|
15
|
+
});
|
|
16
|
+
return { chatEnvelope, standardizedRequest };
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ContextCaptureOptions, ContextCaptureHandler } from './index.js';
|
|
2
|
+
export type ContextSnapshot = Record<string, unknown> | undefined;
|
|
3
|
+
export type ContextCaptureFn = (options: ContextCaptureOptions) => Promise<ContextSnapshot> | ContextSnapshot;
|
|
4
|
+
export declare function createResponsesContextCapture(captureImpl: ContextCaptureHandler): ContextCaptureFn;
|
|
5
|
+
export declare function createNoopContextCapture(label: string): ContextCaptureFn;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { runReqInboundStage3ContextCapture } from './index.js';
|
|
2
|
+
export function createResponsesContextCapture(captureImpl) {
|
|
3
|
+
return (options) => runReqInboundStage3ContextCapture({
|
|
4
|
+
rawRequest: options.rawRequest,
|
|
5
|
+
adapterContext: options.adapterContext,
|
|
6
|
+
stageRecorder: options.stageRecorder,
|
|
7
|
+
captureContext: captureImpl
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export function createNoopContextCapture(label) {
|
|
11
|
+
return (options) => runReqInboundStage3ContextCapture({
|
|
12
|
+
rawRequest: options.rawRequest,
|
|
13
|
+
adapterContext: options.adapterContext,
|
|
14
|
+
stageRecorder: options.stageRecorder,
|
|
15
|
+
captureContext: () => ({ stage: label })
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AdapterContext } from '../../../../types/chat-envelope.js';
|
|
2
|
+
import type { JsonObject } from '../../../../types/json.js';
|
|
3
|
+
import type { StageRecorder } from '../../../../format-adapters/index.js';
|
|
4
|
+
import type { ResponsesRequestContext } from '../../../../../responses/responses-openai-bridge.js';
|
|
5
|
+
export interface ContextCaptureOptions {
|
|
6
|
+
rawRequest: JsonObject;
|
|
7
|
+
adapterContext: AdapterContext;
|
|
8
|
+
stageRecorder?: StageRecorder;
|
|
9
|
+
}
|
|
10
|
+
export type ContextCaptureHandler = (options: ContextCaptureOptions) => Promise<Record<string, unknown> | undefined> | Record<string, unknown> | undefined;
|
|
11
|
+
export interface ReqInboundStage3ContextCaptureOptions {
|
|
12
|
+
rawRequest: JsonObject;
|
|
13
|
+
adapterContext: AdapterContext;
|
|
14
|
+
captureContext?: ContextCaptureHandler;
|
|
15
|
+
stageRecorder?: StageRecorder;
|
|
16
|
+
}
|
|
17
|
+
export declare function runReqInboundStage3ContextCapture(options: ReqInboundStage3ContextCaptureOptions): Promise<Record<string, unknown> | undefined>;
|
|
18
|
+
export declare function runChatContextCapture(options: ContextCaptureOptions): Promise<Record<string, unknown> | undefined>;
|
|
19
|
+
export declare function captureResponsesContextSnapshot(options: ContextCaptureOptions): ResponsesRequestContext;
|