@jsonstudio/llms 0.6.34 → 0.6.54
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 +1 -2
- package/dist/conversion/compat/profiles/chat-glm.json +17 -0
- package/dist/conversion/compat/profiles/chat-iflow.json +36 -0
- package/dist/conversion/compat/profiles/chat-lmstudio.json +37 -0
- package/dist/conversion/compat/profiles/chat-qwen.json +18 -0
- package/dist/conversion/compat/profiles/responses-c4m.json +45 -0
- package/dist/conversion/config/compat-profiles.json +38 -0
- package/dist/conversion/config/sample-config.json +314 -0
- package/dist/conversion/config/version-switch.json +150 -0
- package/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +4 -0
- package/dist/conversion/hub/pipeline/compat/compat-engine.js +667 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-store.d.ts +2 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-store.js +76 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +62 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.js +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +76 -28
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +10 -12
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.d.ts +14 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +23 -0
- package/dist/conversion/hub/response/provider-response.js +18 -0
- package/dist/conversion/hub/response/response-mappers.d.ts +1 -1
- package/dist/conversion/hub/response/response-mappers.js +2 -12
- package/dist/conversion/shared/responses-output-builder.js +22 -43
- package/dist/conversion/shared/responses-response-utils.js +1 -47
- package/dist/conversion/shared/text-markup-normalizer.js +2 -2
- package/dist/conversion/shared/tool-canonicalizer.js +16 -118
- package/dist/conversion/shared/tool-mapping.js +0 -30
- package/dist/filters/config/openai-openai.fieldmap.json +18 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/router/virtual-router/bootstrap.js +18 -33
- package/dist/router/virtual-router/classifier.js +51 -77
- package/dist/router/virtual-router/features.js +338 -111
- package/dist/router/virtual-router/types.d.ts +2 -4
- package/dist/router/virtual-router/types.js +2 -2
- package/dist/sse/sse-to-json/builders/response-builder.js +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const builtinDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../compat/profiles');
|
|
6
|
+
const USER_COMPAT_DIR = (process.env.ROUTECODEX_COMPAT_DIR && process.env.ROUTECODEX_COMPAT_DIR.trim()) ||
|
|
7
|
+
path.join(os.homedir(), '.routecodex', 'compat');
|
|
8
|
+
let profileMap = null;
|
|
9
|
+
function normalizeProfiles(profiles) {
|
|
10
|
+
const map = new Map();
|
|
11
|
+
for (const profile of profiles) {
|
|
12
|
+
if (!profile || typeof profile !== 'object') {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const id = typeof profile.id === 'string' ? profile.id.trim() : '';
|
|
16
|
+
if (!id) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
map.set(id, {
|
|
20
|
+
...profile,
|
|
21
|
+
id
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return map;
|
|
25
|
+
}
|
|
26
|
+
function loadProfilesFromDir(dir) {
|
|
27
|
+
try {
|
|
28
|
+
if (!fs.existsSync(dir)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const entries = fs.readdirSync(dir);
|
|
32
|
+
const configs = [];
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.endsWith('.json')) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const file = path.join(dir, entry);
|
|
38
|
+
try {
|
|
39
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
40
|
+
const json = JSON.parse(text);
|
|
41
|
+
if (!json.id) {
|
|
42
|
+
json.id = entry.replace(/\.json$/i, '');
|
|
43
|
+
}
|
|
44
|
+
configs.push(json);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.warn(`[compat] Failed to load ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return configs;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function buildProfileMap() {
|
|
57
|
+
const merged = new Map();
|
|
58
|
+
const builtinProfiles = normalizeProfiles(loadProfilesFromDir(builtinDir));
|
|
59
|
+
for (const [key, value] of builtinProfiles.entries()) {
|
|
60
|
+
merged.set(key, value);
|
|
61
|
+
}
|
|
62
|
+
const userProfiles = normalizeProfiles(loadProfilesFromDir(USER_COMPAT_DIR));
|
|
63
|
+
for (const [key, value] of userProfiles.entries()) {
|
|
64
|
+
merged.set(key, value);
|
|
65
|
+
}
|
|
66
|
+
return merged;
|
|
67
|
+
}
|
|
68
|
+
export function getCompatProfile(profileId) {
|
|
69
|
+
if (!profileId || !profileId.trim()) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (!profileMap) {
|
|
73
|
+
profileMap = buildProfileMap();
|
|
74
|
+
}
|
|
75
|
+
return profileMap.get(profileId.trim()) ?? null;
|
|
76
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { JsonObject, JsonValue } from '../../types/json.js';
|
|
2
|
+
export type CompatDirection = 'request' | 'response';
|
|
3
|
+
export interface CompatProfileConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
protocol: string;
|
|
6
|
+
direction?: CompatDirection;
|
|
7
|
+
mappings?: MappingInstruction[];
|
|
8
|
+
filters?: FilterInstruction[];
|
|
9
|
+
request?: CompatStageConfig;
|
|
10
|
+
response?: CompatStageConfig;
|
|
11
|
+
}
|
|
12
|
+
export interface CompatStageConfig {
|
|
13
|
+
mappings?: MappingInstruction[];
|
|
14
|
+
filters?: FilterInstruction[];
|
|
15
|
+
}
|
|
16
|
+
export type MappingInstruction = {
|
|
17
|
+
action: 'remove';
|
|
18
|
+
path: string;
|
|
19
|
+
} | {
|
|
20
|
+
action: 'rename';
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
} | {
|
|
24
|
+
action: 'set';
|
|
25
|
+
path: string;
|
|
26
|
+
value: JsonValue;
|
|
27
|
+
} | {
|
|
28
|
+
action: 'stringify';
|
|
29
|
+
path: string;
|
|
30
|
+
fallback?: JsonValue;
|
|
31
|
+
} | {
|
|
32
|
+
action: 'set_default';
|
|
33
|
+
path: string;
|
|
34
|
+
value?: JsonValue;
|
|
35
|
+
valueSource?: 'timestamp_seconds' | 'chat_completion_id';
|
|
36
|
+
} | {
|
|
37
|
+
action: 'normalize_tool_choice';
|
|
38
|
+
path?: string;
|
|
39
|
+
objectReplacement?: string;
|
|
40
|
+
} | {
|
|
41
|
+
action: 'inject_instruction';
|
|
42
|
+
sourcePath: string;
|
|
43
|
+
targetPath?: string;
|
|
44
|
+
role?: string;
|
|
45
|
+
contentType?: string;
|
|
46
|
+
stripHtml?: boolean;
|
|
47
|
+
maxLengthEnv?: string[];
|
|
48
|
+
} | {
|
|
49
|
+
action: 'parse_json';
|
|
50
|
+
path: string;
|
|
51
|
+
fallback?: JsonValue;
|
|
52
|
+
} | {
|
|
53
|
+
action: 'convert_responses_output_to_choices';
|
|
54
|
+
};
|
|
55
|
+
export type FilterInstruction = {
|
|
56
|
+
action: 'rate_limit_text';
|
|
57
|
+
needle: string;
|
|
58
|
+
};
|
|
59
|
+
export interface CompatApplicationResult {
|
|
60
|
+
payload: JsonObject;
|
|
61
|
+
appliedProfile?: string;
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -18,6 +18,7 @@ import { runReqProcessStage1ToolGovernance } from './stages/req_process/req_proc
|
|
|
18
18
|
import { runReqProcessStage2RouteSelect } from './stages/req_process/req_process_stage2_route_select/index.js';
|
|
19
19
|
import { runReqOutboundStage1SemanticMap } from './stages/req_outbound/req_outbound_stage1_semantic_map/index.js';
|
|
20
20
|
import { runReqOutboundStage2FormatBuild } from './stages/req_outbound/req_outbound_stage2_format_build/index.js';
|
|
21
|
+
import { runReqOutboundStage3Compat } from './stages/req_outbound/req_outbound_stage3_compat/index.js';
|
|
21
22
|
export class HubPipeline {
|
|
22
23
|
routerEngine;
|
|
23
24
|
config;
|
|
@@ -109,6 +110,9 @@ export class HubPipeline {
|
|
|
109
110
|
stageRecorder: inboundRecorder
|
|
110
111
|
});
|
|
111
112
|
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
113
|
+
if (routing.target?.compatibilityProfile) {
|
|
114
|
+
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
115
|
+
}
|
|
112
116
|
const outboundProtocol = outboundAdapterContext.providerProtocol;
|
|
113
117
|
const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
|
|
114
118
|
const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
|
|
@@ -122,35 +126,66 @@ export class HubPipeline {
|
|
|
122
126
|
const outboundEndpoint = resolveEndpointForProviderProtocol(outboundAdapterContext.providerProtocol);
|
|
123
127
|
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, outboundEndpoint);
|
|
124
128
|
const outboundStart = Date.now();
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
executionTime: outboundEnd - outboundStart,
|
|
146
|
-
startTime: outboundStart,
|
|
147
|
-
endTime: outboundEnd,
|
|
148
|
-
dataProcessed: {
|
|
149
|
-
messages: workingRequest.messages.length,
|
|
150
|
-
tools: workingRequest.tools?.length ?? 0
|
|
129
|
+
const isResponsesSubmit = normalized.entryEndpoint === '/v1/responses.submit_tool_outputs' &&
|
|
130
|
+
outboundProtocol === 'openai-responses';
|
|
131
|
+
let providerPayload;
|
|
132
|
+
if (isResponsesSubmit) {
|
|
133
|
+
providerPayload =
|
|
134
|
+
this.pickRawRequestBody(normalized.metadata) ?? normalized.payload;
|
|
135
|
+
const outboundEnd = Date.now();
|
|
136
|
+
nodeResults.push({
|
|
137
|
+
id: 'req_outbound',
|
|
138
|
+
success: true,
|
|
139
|
+
metadata: {
|
|
140
|
+
node: 'req_outbound',
|
|
141
|
+
executionTime: outboundEnd - outboundStart,
|
|
142
|
+
startTime: outboundStart,
|
|
143
|
+
endTime: outboundEnd,
|
|
144
|
+
dataProcessed: {
|
|
145
|
+
messages: 0,
|
|
146
|
+
tools: 0
|
|
147
|
+
},
|
|
148
|
+
bypass: 'responses-submit-passthrough'
|
|
151
149
|
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
154
|
+
request: workingRequest,
|
|
155
|
+
adapterContext: outboundAdapterContext,
|
|
156
|
+
semanticMapper: outboundSemanticMapper,
|
|
157
|
+
contextSnapshot: outboundContextSnapshot,
|
|
158
|
+
contextMetadataKey: outboundContextMetadataKey,
|
|
159
|
+
stageRecorder: outboundRecorder
|
|
160
|
+
});
|
|
161
|
+
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
162
|
+
formatEnvelope: outboundStage1.formatEnvelope,
|
|
163
|
+
adapterContext: outboundAdapterContext,
|
|
164
|
+
formatAdapter: outboundFormatAdapter,
|
|
165
|
+
stageRecorder: outboundRecorder
|
|
166
|
+
});
|
|
167
|
+
formattedPayload = await runReqOutboundStage3Compat({
|
|
168
|
+
payload: formattedPayload,
|
|
169
|
+
adapterContext: outboundAdapterContext,
|
|
170
|
+
stageRecorder: outboundRecorder
|
|
171
|
+
});
|
|
172
|
+
providerPayload = formattedPayload;
|
|
173
|
+
const outboundEnd = Date.now();
|
|
174
|
+
nodeResults.push({
|
|
175
|
+
id: 'req_outbound',
|
|
176
|
+
success: true,
|
|
177
|
+
metadata: {
|
|
178
|
+
node: 'req_outbound',
|
|
179
|
+
executionTime: outboundEnd - outboundStart,
|
|
180
|
+
startTime: outboundStart,
|
|
181
|
+
endTime: outboundEnd,
|
|
182
|
+
dataProcessed: {
|
|
183
|
+
messages: workingRequest.messages.length,
|
|
184
|
+
tools: workingRequest.tools?.length ?? 0
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
154
189
|
const metadata = {
|
|
155
190
|
...normalized.metadata,
|
|
156
191
|
entryEndpoint: normalized.entryEndpoint,
|
|
@@ -271,6 +306,9 @@ export class HubPipeline {
|
|
|
271
306
|
if (typeof metadata.assignedModelId === 'string') {
|
|
272
307
|
adapterContext.modelId = metadata.assignedModelId;
|
|
273
308
|
}
|
|
309
|
+
if (target?.compatibilityProfile && typeof target.compatibilityProfile === 'string') {
|
|
310
|
+
adapterContext.compatibilityProfile = target.compatibilityProfile;
|
|
311
|
+
}
|
|
274
312
|
return adapterContext;
|
|
275
313
|
}
|
|
276
314
|
maybeCreateStageRecorder(context, endpoint) {
|
|
@@ -292,6 +330,16 @@ export class HubPipeline {
|
|
|
292
330
|
}
|
|
293
331
|
return value;
|
|
294
332
|
}
|
|
333
|
+
pickRawRequestBody(metadata) {
|
|
334
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
const raw = metadata.__raw_request_body;
|
|
338
|
+
if (!raw || typeof raw !== 'object') {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
return raw;
|
|
342
|
+
}
|
|
295
343
|
async normalizeRequest(request) {
|
|
296
344
|
if (!request || typeof request !== 'object') {
|
|
297
345
|
throw new Error('HubPipeline requires request payload');
|
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js
CHANGED
|
@@ -19,18 +19,6 @@ export async function runReqInboundStage3ContextCapture(options) {
|
|
|
19
19
|
? augmentContextSnapshot(context, fallbackSnapshot)
|
|
20
20
|
: fallbackSnapshot;
|
|
21
21
|
recordStage(options.stageRecorder, 'req_inbound_stage3_context_capture', snapshot);
|
|
22
|
-
if (options.adapterContext.providerProtocol === 'openai-responses') {
|
|
23
|
-
try {
|
|
24
|
-
captureResponsesRequestContext({
|
|
25
|
-
requestId: options.adapterContext.requestId,
|
|
26
|
-
payload: options.rawRequest,
|
|
27
|
-
context: snapshot
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
/* ignore capture failures */
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
22
|
return context ?? snapshot;
|
|
35
23
|
}
|
|
36
24
|
export function runChatContextCapture(options) {
|
|
@@ -66,6 +54,16 @@ export function captureResponsesContextSnapshot(options) {
|
|
|
66
54
|
catch {
|
|
67
55
|
// best-effort context capture
|
|
68
56
|
}
|
|
57
|
+
try {
|
|
58
|
+
captureResponsesRequestContext({
|
|
59
|
+
requestId: options.adapterContext.requestId,
|
|
60
|
+
payload: options.rawRequest,
|
|
61
|
+
context
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// ignore store capture failures
|
|
66
|
+
}
|
|
69
67
|
return context;
|
|
70
68
|
}
|
|
71
69
|
function captureChatContextSnapshot(options) {
|
package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AdapterContext } from '../../../../types/chat-envelope.js';
|
|
2
|
+
import type { StageRecorder } from '../../../../format-adapters/index.js';
|
|
3
|
+
import type { JsonObject } from '../../../../types/json.js';
|
|
4
|
+
export type ProviderPayload = JsonObject;
|
|
5
|
+
export declare function runReqOutboundStage3Compat(options: {
|
|
6
|
+
payload: ProviderPayload;
|
|
7
|
+
adapterContext: AdapterContext;
|
|
8
|
+
stageRecorder?: StageRecorder;
|
|
9
|
+
}): Promise<ProviderPayload>;
|
|
10
|
+
export declare function runRespInboundStageCompatResponse(options: {
|
|
11
|
+
payload: ProviderPayload;
|
|
12
|
+
adapterContext: AdapterContext;
|
|
13
|
+
stageRecorder?: StageRecorder;
|
|
14
|
+
}): ProviderPayload;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { applyRequestCompat, applyResponseCompat } from '../../../compat/compat-engine.js';
|
|
2
|
+
function pickCompatProfile(adapterContext) {
|
|
3
|
+
const candidate = adapterContext.compatibilityProfile;
|
|
4
|
+
return typeof candidate === 'string' && candidate.trim() ? candidate.trim() : undefined;
|
|
5
|
+
}
|
|
6
|
+
export async function runReqOutboundStage3Compat(options) {
|
|
7
|
+
const profile = pickCompatProfile(options.adapterContext);
|
|
8
|
+
const result = applyRequestCompat(profile, options.payload);
|
|
9
|
+
options.stageRecorder?.record('req_outbound_stage3_compat', {
|
|
10
|
+
applied: Boolean(result.appliedProfile),
|
|
11
|
+
profile: result.appliedProfile || profile || 'passthrough'
|
|
12
|
+
});
|
|
13
|
+
return result.payload;
|
|
14
|
+
}
|
|
15
|
+
export function runRespInboundStageCompatResponse(options) {
|
|
16
|
+
const profile = pickCompatProfile(options.adapterContext);
|
|
17
|
+
const result = applyResponseCompat(profile, options.payload);
|
|
18
|
+
options.stageRecorder?.record('resp_inbound_stage_compat', {
|
|
19
|
+
applied: Boolean(result.appliedProfile),
|
|
20
|
+
profile: result.appliedProfile || profile || 'passthrough'
|
|
21
|
+
});
|
|
22
|
+
return result.payload;
|
|
23
|
+
}
|
|
@@ -6,10 +6,12 @@ import { OpenAIChatResponseMapper, ResponsesResponseMapper, AnthropicResponseMap
|
|
|
6
6
|
import { runRespInboundStage1SseDecode } from '../pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js';
|
|
7
7
|
import { runRespInboundStage2FormatParse } from '../pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js';
|
|
8
8
|
import { runRespInboundStage3SemanticMap } from '../pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js';
|
|
9
|
+
import { runRespInboundStageCompatResponse } from '../pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js';
|
|
9
10
|
import { runRespProcessStage1ToolGovernance } from '../pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js';
|
|
10
11
|
import { runRespProcessStage2Finalize } from '../pipeline/stages/resp_process/resp_process_stage2_finalize/index.js';
|
|
11
12
|
import { runRespOutboundStage1ClientRemap } from '../pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js';
|
|
12
13
|
import { runRespOutboundStage2SseStream } from '../pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js';
|
|
14
|
+
import { recordResponsesResponse } from '../../shared/responses-conversation-store.js';
|
|
13
15
|
function resolveChatReasoningMode(entryEndpoint) {
|
|
14
16
|
const envRaw = (process.env.ROUTECODEX_CHAT_REASONING_MODE || process.env.RCC_CHAT_REASONING_MODE || '').trim().toLowerCase();
|
|
15
17
|
const map = {
|
|
@@ -105,6 +107,22 @@ export async function convertProviderResponse(options) {
|
|
|
105
107
|
formatAdapter,
|
|
106
108
|
stageRecorder: options.stageRecorder
|
|
107
109
|
});
|
|
110
|
+
if (options.providerProtocol === 'openai-responses') {
|
|
111
|
+
try {
|
|
112
|
+
recordResponsesResponse({
|
|
113
|
+
requestId: options.context.requestId,
|
|
114
|
+
response: formatEnvelope.payload
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// ignore conversation capture errors
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
formatEnvelope.payload = runRespInboundStageCompatResponse({
|
|
122
|
+
payload: formatEnvelope.payload,
|
|
123
|
+
adapterContext: options.context,
|
|
124
|
+
stageRecorder: options.stageRecorder
|
|
125
|
+
});
|
|
108
126
|
const chatResponse = await runRespInboundStage3SemanticMap({
|
|
109
127
|
adapterContext: options.context,
|
|
110
128
|
formatEnvelope,
|
|
@@ -9,7 +9,7 @@ export declare class OpenAIChatResponseMapper implements ResponseMapper {
|
|
|
9
9
|
toChatCompletion(format: FormatEnvelope, _ctx: AdapterContext): ChatCompletionLike;
|
|
10
10
|
}
|
|
11
11
|
export declare class ResponsesResponseMapper implements ResponseMapper {
|
|
12
|
-
toChatCompletion(format: FormatEnvelope,
|
|
12
|
+
toChatCompletion(format: FormatEnvelope, _ctx: AdapterContext): ChatCompletionLike;
|
|
13
13
|
}
|
|
14
14
|
export declare class AnthropicResponseMapper implements ResponseMapper {
|
|
15
15
|
toChatCompletion(format: FormatEnvelope, ctx: AdapterContext): ChatCompletionLike;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { buildOpenAIChatFromGeminiResponse } from '../../codecs/gemini-openai-codec.js';
|
|
2
2
|
import { buildChatResponseFromResponses } from '../../shared/responses-response-utils.js';
|
|
3
|
-
import { recordResponsesResponse } from '../../shared/responses-conversation-store.js';
|
|
4
3
|
import { buildOpenAIChatFromAnthropicMessage } from './response-runtime.js';
|
|
5
4
|
export class OpenAIChatResponseMapper {
|
|
6
5
|
toChatCompletion(format, _ctx) {
|
|
@@ -8,17 +7,8 @@ export class OpenAIChatResponseMapper {
|
|
|
8
7
|
}
|
|
9
8
|
}
|
|
10
9
|
export class ResponsesResponseMapper {
|
|
11
|
-
toChatCompletion(format,
|
|
12
|
-
|
|
13
|
-
if (ctx?.requestId) {
|
|
14
|
-
try {
|
|
15
|
-
recordResponsesResponse({ requestId: ctx.requestId, response: payload });
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
/* ignore capture failures */
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return buildChatResponseFromResponses(payload);
|
|
10
|
+
toChatCompletion(format, _ctx) {
|
|
11
|
+
return buildChatResponseFromResponses(format.payload ?? {});
|
|
22
12
|
}
|
|
23
13
|
}
|
|
24
14
|
export class AnthropicResponseMapper {
|
|
@@ -112,50 +112,29 @@ export function buildResponsesOutputFromChat(options) {
|
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
function normalizeUsage(usageRaw) {
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
source.prompt_tokens_details);
|
|
137
|
-
if (inputDetails) {
|
|
138
|
-
usage.input_tokens_details = inputDetails;
|
|
139
|
-
}
|
|
140
|
-
const outputDetails = extractTokenDetails(source.output_tokens_details ??
|
|
141
|
-
source.completion_tokens_details);
|
|
142
|
-
if (outputDetails) {
|
|
143
|
-
usage.output_tokens_details = outputDetails;
|
|
144
|
-
}
|
|
145
|
-
return Object.keys(usage).length ? usage : undefined;
|
|
146
|
-
}
|
|
147
|
-
function normalizeNumber(value) {
|
|
148
|
-
if (value == null) {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
const parsed = Number(value);
|
|
152
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
153
|
-
}
|
|
154
|
-
function extractTokenDetails(raw) {
|
|
155
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
156
|
-
return undefined;
|
|
115
|
+
if (usageRaw && typeof usageRaw === 'object') {
|
|
116
|
+
const usage = { ...usageRaw };
|
|
117
|
+
if (usage.input_tokens != null && usage.prompt_tokens == null) {
|
|
118
|
+
usage.prompt_tokens = usage.input_tokens;
|
|
119
|
+
}
|
|
120
|
+
if (usage.output_tokens != null && usage.completion_tokens == null) {
|
|
121
|
+
usage.completion_tokens = usage.output_tokens;
|
|
122
|
+
}
|
|
123
|
+
if (usage.prompt_tokens != null && usage.completion_tokens != null && usage.total_tokens == null) {
|
|
124
|
+
const total = Number(usage.prompt_tokens) + Number(usage.completion_tokens);
|
|
125
|
+
if (!Number.isNaN(total))
|
|
126
|
+
usage.total_tokens = total;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
delete usage.input_tokens;
|
|
130
|
+
delete usage.output_tokens;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
/* ignore */
|
|
134
|
+
}
|
|
135
|
+
return usage;
|
|
157
136
|
}
|
|
158
|
-
return
|
|
137
|
+
return usageRaw;
|
|
159
138
|
}
|
|
160
139
|
function buildFunctionCallOutput(call, allocateOutputId, sanitizeFunctionName, baseCount, offset) {
|
|
161
140
|
try {
|
|
@@ -167,7 +167,7 @@ export function buildChatResponseFromResponses(payload) {
|
|
|
167
167
|
const created = typeof response.created_at === 'number'
|
|
168
168
|
? response.created_at
|
|
169
169
|
: (response.created ?? Math.floor(Date.now() / 1000));
|
|
170
|
-
const usage =
|
|
170
|
+
const usage = response.usage;
|
|
171
171
|
const toolCalls = collectToolCallsFromResponses(response);
|
|
172
172
|
const { textParts, reasoningParts } = extractOutputSegments(response);
|
|
173
173
|
const rawReasoningSegments = collectRawReasoningSegments(response);
|
|
@@ -237,49 +237,3 @@ export function buildChatResponseFromResponses(payload) {
|
|
|
237
237
|
}
|
|
238
238
|
return chat;
|
|
239
239
|
}
|
|
240
|
-
function mapResponsesUsageToChat(usageRaw) {
|
|
241
|
-
if (!usageRaw || typeof usageRaw !== 'object') {
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
const source = usageRaw;
|
|
245
|
-
const usage = {};
|
|
246
|
-
const promptValue = toNumber(source.prompt_tokens ?? source.input_tokens);
|
|
247
|
-
if (promptValue !== undefined) {
|
|
248
|
-
usage.prompt_tokens = promptValue;
|
|
249
|
-
}
|
|
250
|
-
const completionValue = toNumber(source.completion_tokens ?? source.output_tokens);
|
|
251
|
-
if (completionValue !== undefined) {
|
|
252
|
-
usage.completion_tokens = completionValue;
|
|
253
|
-
}
|
|
254
|
-
const totalValue = toNumber(source.total_tokens);
|
|
255
|
-
if (totalValue !== undefined) {
|
|
256
|
-
usage.total_tokens = totalValue;
|
|
257
|
-
}
|
|
258
|
-
else if (promptValue !== undefined && completionValue !== undefined) {
|
|
259
|
-
usage.total_tokens = promptValue + completionValue;
|
|
260
|
-
}
|
|
261
|
-
const promptDetails = extractDetailObject(source.prompt_tokens_details ??
|
|
262
|
-
source.input_tokens_details);
|
|
263
|
-
if (promptDetails) {
|
|
264
|
-
usage.prompt_tokens_details = promptDetails;
|
|
265
|
-
}
|
|
266
|
-
const completionDetails = extractDetailObject(source.completion_tokens_details ??
|
|
267
|
-
source.output_tokens_details);
|
|
268
|
-
if (completionDetails) {
|
|
269
|
-
usage.completion_tokens_details = completionDetails;
|
|
270
|
-
}
|
|
271
|
-
return Object.keys(usage).length ? usage : undefined;
|
|
272
|
-
}
|
|
273
|
-
function toNumber(value) {
|
|
274
|
-
if (value == null) {
|
|
275
|
-
return undefined;
|
|
276
|
-
}
|
|
277
|
-
const parsed = Number(value);
|
|
278
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
279
|
-
}
|
|
280
|
-
function extractDetailObject(raw) {
|
|
281
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
284
|
-
return { ...raw };
|
|
285
|
-
}
|
|
@@ -95,10 +95,10 @@ export function extractApplyPatchCallsFromText(text) {
|
|
|
95
95
|
continue;
|
|
96
96
|
let argsStr = '{}';
|
|
97
97
|
try {
|
|
98
|
-
argsStr = JSON.stringify({
|
|
98
|
+
argsStr = JSON.stringify({ patch });
|
|
99
99
|
}
|
|
100
100
|
catch {
|
|
101
|
-
argsStr = '{"
|
|
101
|
+
argsStr = '{"patch":""}';
|
|
102
102
|
}
|
|
103
103
|
out.push({ id: genId(), name: 'apply_patch', args: argsStr });
|
|
104
104
|
}
|