@jsonstudio/llms 0.6.3551 → 0.6.3686
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/compat/actions/antigravity-thought-signature-cache.js +4 -115
- package/dist/conversion/compat/actions/auto-thinking.js +3 -2
- package/dist/conversion/compat/actions/deepseek-web-response.js +15 -49
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +2 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +1 -1
- package/dist/conversion/compat/actions/glm-history-image-trim.js +3 -37
- package/dist/conversion/compat/actions/glm-image-content.js +3 -32
- package/dist/conversion/compat/actions/glm-native-compat.d.ts +6 -0
- package/dist/conversion/compat/actions/glm-native-compat.js +34 -0
- package/dist/conversion/compat/actions/glm-vision-prompt.js +3 -76
- package/dist/conversion/compat/actions/glm-web-search.js +10 -43
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +4 -53
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +5 -141
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +7 -28
- package/dist/conversion/compat/actions/iflow-native-compat.d.ts +6 -0
- package/dist/conversion/compat/actions/iflow-native-compat.js +36 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +4 -119
- package/dist/conversion/compat/actions/iflow-web-search.js +14 -55
- package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.js +5 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +31 -18
- package/dist/conversion/hub/pipeline/hub-pipeline.js +163 -163
- package/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-stage-timing.js +178 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.d.ts +4 -4
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +33 -14
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -6
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +41 -23
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +44 -1
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -1
- package/dist/conversion/hub/process/chat-process-continue-execution.js +5 -4
- package/dist/conversion/hub/process/chat-process-governance-orchestration.js +3 -1
- package/dist/conversion/hub/process/chat-process-media.d.ts +3 -1
- package/dist/conversion/hub/process/chat-process-media.js +92 -2
- package/dist/conversion/hub/process/chat-process-session-usage.d.ts +7 -0
- package/dist/conversion/hub/process/chat-process-session-usage.js +147 -0
- package/dist/conversion/hub/response/provider-response.js +13 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +0 -12
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -9
- package/dist/conversion/responses/responses-openai-bridge.js +77 -44
- package/dist/conversion/shared/reasoning-normalizer.js +42 -0
- package/dist/conversion/shared/responses-tool-utils.js +2 -3
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +1 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +1 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +6 -0
- package/dist/router/virtual-router/bootstrap.js +1 -6
- package/dist/router/virtual-router/engine-legacy.js +43 -0
- package/dist/router/virtual-router/engine-logging.d.ts +3 -0
- package/dist/router/virtual-router/engine-logging.js +29 -3
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.d.ts +2 -2
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js +96 -80
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-process-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine.js +34 -22
- package/dist/router/virtual-router/provider-registry.js +1 -0
- package/dist/router/virtual-router/routing-instructions/state.js +35 -3
- package/dist/router/virtual-router/routing-instructions/types.d.ts +4 -0
- package/dist/router/virtual-router/types.d.ts +7 -0
- package/dist/servertool/engine.js +3 -34
- package/dist/servertool/handlers/followup-request-builder.js +0 -6
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +3 -274
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +0 -3
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +0 -29
- package/dist/servertool/handlers/stop-message-auto.js +2 -10
- package/dist/servertool/handlers/vision.js +4 -1
- package/dist/servertool/server-side-tools.js +66 -3
- package/package.json +1 -1
|
@@ -1,119 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function resolveStableSessionId(adapterContext) {
|
|
6
|
-
if (!adapterContext) {
|
|
7
|
-
return undefined;
|
|
8
|
-
}
|
|
9
|
-
const ctxAny = adapterContext;
|
|
10
|
-
const candidates = [ctxAny.sessionId, ctxAny.conversationId].filter((v) => typeof v === 'string');
|
|
11
|
-
const raw = candidates.map((s) => s.trim()).find((s) => s.length > 0);
|
|
12
|
-
if (!raw) {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
// Antigravity-Manager alignment: never hash/derive session ids from external session/conversation identifiers here.
|
|
16
|
-
// If the caller already provides a proper fingerprint (sid-*), we can use it as a last-resort fallback.
|
|
17
|
-
return raw.toLowerCase().startsWith('sid-') ? raw : undefined;
|
|
18
|
-
}
|
|
19
|
-
function resolveAntigravityAliasKey(adapterContext) {
|
|
20
|
-
if (!adapterContext) {
|
|
21
|
-
return 'antigravity.unknown';
|
|
22
|
-
}
|
|
23
|
-
const ctxAny = adapterContext;
|
|
24
|
-
const candidates = [ctxAny.runtimeKey, ctxAny.providerKey, ctxAny.providerId].filter((v) => typeof v === 'string');
|
|
25
|
-
for (const value of candidates) {
|
|
26
|
-
const trimmed = value.trim();
|
|
27
|
-
if (!trimmed)
|
|
28
|
-
continue;
|
|
29
|
-
const lower = trimmed.toLowerCase();
|
|
30
|
-
if (lower.startsWith('antigravity.')) {
|
|
31
|
-
const parts = trimmed.split('.');
|
|
32
|
-
if (parts.length >= 2 && parts[0] && parts[1]) {
|
|
33
|
-
return `${parts[0].trim()}.${parts[1].trim()}`;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return trimmed;
|
|
37
|
-
}
|
|
38
|
-
return 'antigravity.unknown';
|
|
39
|
-
}
|
|
40
|
-
function shouldEnableForAdapter(adapterContext) {
|
|
41
|
-
if (!adapterContext) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
const protocol = typeof adapterContext.providerProtocol === 'string' ? adapterContext.providerProtocol.trim().toLowerCase() : '';
|
|
45
|
-
if (protocol !== 'gemini-chat') {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
const ctxAny = adapterContext;
|
|
49
|
-
const providerIdOrKeyRaw = typeof ctxAny.providerId === 'string'
|
|
50
|
-
? String(ctxAny.providerId)
|
|
51
|
-
: typeof ctxAny.providerKey === 'string'
|
|
52
|
-
? String(ctxAny.providerKey)
|
|
53
|
-
: typeof ctxAny.runtimeKey === 'string'
|
|
54
|
-
? String(ctxAny.runtimeKey)
|
|
55
|
-
: '';
|
|
56
|
-
const providerIdOrKey = providerIdOrKeyRaw.trim().toLowerCase();
|
|
57
|
-
const effectiveProviderId = providerIdOrKey.split('.')[0] ?? '';
|
|
58
|
-
// Antigravity-Manager alignment: thoughtSignature compat applies to both Antigravity and Gemini CLI.
|
|
59
|
-
return effectiveProviderId === 'antigravity' || effectiveProviderId === 'gemini-cli';
|
|
60
|
-
}
|
|
1
|
+
import { runRespInboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGeminiCliCompatInput } from './gemini-cli-request.js';
|
|
61
3
|
export function cacheAntigravityThoughtSignatureFromGeminiResponse(payload, adapterContext) {
|
|
62
|
-
if (!
|
|
4
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
63
5
|
return payload;
|
|
64
6
|
}
|
|
65
|
-
|
|
66
|
-
const ctxAny = adapterContext;
|
|
67
|
-
const payloadAny = payload;
|
|
68
|
-
const keyCandidates = [
|
|
69
|
-
adapterContext?.requestId,
|
|
70
|
-
typeof ctxAny.clientRequestId === 'string' ? String(ctxAny.clientRequestId) : '',
|
|
71
|
-
typeof ctxAny.groupRequestId === 'string' ? String(ctxAny.groupRequestId) : '',
|
|
72
|
-
typeof payloadAny.request_id === 'string' ? String(payloadAny.request_id) : '',
|
|
73
|
-
typeof payloadAny.requestId === 'string' ? String(payloadAny.requestId) : ''
|
|
74
|
-
].filter((k) => typeof k === 'string' && k.trim().length);
|
|
75
|
-
let aliasKey = fallbackAliasKey;
|
|
76
|
-
let sessionId = '';
|
|
77
|
-
let messageCount = 1;
|
|
78
|
-
for (const key of keyCandidates) {
|
|
79
|
-
const resolved = getAntigravityRequestSessionMeta(key);
|
|
80
|
-
if (resolved && resolved.sessionId.trim().length) {
|
|
81
|
-
aliasKey = typeof resolved.aliasKey === 'string' && resolved.aliasKey.trim().length ? resolved.aliasKey.trim() : fallbackAliasKey;
|
|
82
|
-
sessionId = resolved.sessionId.trim();
|
|
83
|
-
messageCount = resolved.messageCount;
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!sessionId) {
|
|
88
|
-
const stable = resolveStableSessionId(adapterContext);
|
|
89
|
-
if (stable) {
|
|
90
|
-
sessionId = stable;
|
|
91
|
-
messageCount = 1;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (!sessionId) {
|
|
95
|
-
return payload;
|
|
96
|
-
}
|
|
97
|
-
const candidatesRaw = payload.candidates;
|
|
98
|
-
const candidates = Array.isArray(candidatesRaw) ? candidatesRaw : [];
|
|
99
|
-
for (const candidate of candidates) {
|
|
100
|
-
const content = isRecord(candidate.content) ? candidate.content : undefined;
|
|
101
|
-
const partsRaw = content?.parts;
|
|
102
|
-
const parts = Array.isArray(partsRaw) ? partsRaw : [];
|
|
103
|
-
for (const part of parts) {
|
|
104
|
-
const sig = typeof part.thoughtSignature === 'string'
|
|
105
|
-
? String(part.thoughtSignature)
|
|
106
|
-
: typeof part.thought_signature === 'string'
|
|
107
|
-
? String(part.thought_signature)
|
|
108
|
-
: '';
|
|
109
|
-
if (sig.trim().length) {
|
|
110
|
-
cacheAntigravitySessionSignature(aliasKey, sessionId, sig.trim(), messageCount);
|
|
111
|
-
// Antigravity-Manager alignment: also store into the global signature store for this session.
|
|
112
|
-
if (aliasKey !== ANTIGRAVITY_GLOBAL_ALIAS_KEY) {
|
|
113
|
-
cacheAntigravitySessionSignature(ANTIGRAVITY_GLOBAL_ALIAS_KEY, sessionId, sig.trim(), messageCount);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return payload;
|
|
7
|
+
return runRespInboundStage3CompatWithNative(buildGeminiCliCompatInput(payload, adapterContext)).payload;
|
|
119
8
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGlmRequestCompatInput } from './glm-native-compat.js';
|
|
1
3
|
export function applyAutoThinking(payload, config) {
|
|
2
4
|
if (!config) {
|
|
3
5
|
return payload;
|
|
@@ -20,6 +22,5 @@ export function applyAutoThinking(payload, config) {
|
|
|
20
22
|
if (thinkingNode && typeof thinkingNode === 'object') {
|
|
21
23
|
return payload;
|
|
22
24
|
}
|
|
23
|
-
|
|
24
|
-
return payload;
|
|
25
|
+
return runReqOutboundStage3CompatWithNative(buildGlmRequestCompatInput(payload)).payload;
|
|
25
26
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { buildNativeReqOutboundCompatAdapterContext } from '../../hub/pipeline/compat/native-adapter-context.js';
|
|
2
|
-
import {
|
|
2
|
+
import { runRespInboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
3
3
|
import { isNativeDisabledByEnv, makeNativeRequiredError } from '../../../router/virtual-router/engine-selection/native-router-hotpath-policy.js';
|
|
4
4
|
import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
|
|
5
|
-
const CAPABILITY = 'runRespInboundStage3CompatJson';
|
|
6
5
|
const PROFILE = 'chat:deepseek-web';
|
|
7
6
|
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
8
7
|
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
@@ -98,62 +97,29 @@ function buildCompatInput(payload, adapterContext, config) {
|
|
|
98
97
|
explicitProfile: PROFILE
|
|
99
98
|
};
|
|
100
99
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
108
|
-
}
|
|
109
|
-
if (!isRecord(parsed) || !isRecord(parsed.payload) || typeof parsed.nativeApplied !== 'boolean') {
|
|
110
|
-
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
111
|
-
}
|
|
112
|
-
return parsed;
|
|
113
|
-
}
|
|
114
|
-
function callDeepSeekWebResponseCompat(input, adapterContext) {
|
|
115
|
-
if (isNativeDisabledByEnv()) {
|
|
116
|
-
emitCompatError(makeNativeRequiredError(CAPABILITY, 'native disabled'), adapterContext, input.payload, {
|
|
117
|
-
reason: 'native disabled'
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
const binding = loadNativeRouterHotpathBindingForInternalUse();
|
|
121
|
-
const fn = binding?.[CAPABILITY];
|
|
122
|
-
if (typeof fn !== 'function') {
|
|
123
|
-
emitCompatError(makeNativeRequiredError(CAPABILITY), adapterContext, input.payload, {
|
|
124
|
-
reason: 'missing native export'
|
|
100
|
+
export function applyDeepSeekWebResponseTransform(payload, adapterContext, config) {
|
|
101
|
+
if (!payload || typeof payload !== 'object') {
|
|
102
|
+
emitCompatError(new Error('[deepseek-web] invalid compat payload: expected object'), adapterContext, payload, {
|
|
103
|
+
reason: 'payload is not an object'
|
|
125
104
|
});
|
|
126
105
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
106
|
+
// Fail fast if response is missing required shape (choices array)
|
|
107
|
+
if (!Array.isArray(payload.choices)) {
|
|
108
|
+
// Allow business error format (code + msg) to pass through for separate handling
|
|
109
|
+
if (!('code' in payload && 'msg' in payload)) {
|
|
110
|
+
emitCompatError(new Error('[deepseek-web] invalid response: missing required "choices" array'), adapterContext, payload, { reason: 'missing required response shape' });
|
|
111
|
+
}
|
|
130
112
|
}
|
|
131
|
-
|
|
132
|
-
emitCompatError(makeNativeRequiredError(
|
|
133
|
-
reason: 'json stringify failed'
|
|
134
|
-
});
|
|
113
|
+
if (isNativeDisabledByEnv()) {
|
|
114
|
+
emitCompatError(makeNativeRequiredError('runRespInboundStage3CompatJson', 'native disabled'), adapterContext, payload, { reason: 'native disabled' });
|
|
135
115
|
}
|
|
136
116
|
try {
|
|
137
|
-
|
|
138
|
-
if (typeof raw !== 'string' || !raw) {
|
|
139
|
-
emitCompatError(makeNativeRequiredError(CAPABILITY, 'empty result'), adapterContext, input.payload, {
|
|
140
|
-
reason: 'empty result'
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
return parseCompatOutput(raw);
|
|
117
|
+
return runRespInboundStage3CompatWithNative(buildCompatInput(payload, adapterContext, config)).payload;
|
|
144
118
|
}
|
|
145
119
|
catch (error) {
|
|
146
120
|
const compatError = error instanceof Error ? error : new Error(String(error));
|
|
147
|
-
emitCompatError(compatError, adapterContext,
|
|
121
|
+
emitCompatError(compatError, adapterContext, payload, {
|
|
148
122
|
reason: 'native compat execution failed'
|
|
149
123
|
});
|
|
150
124
|
}
|
|
151
125
|
}
|
|
152
|
-
export function applyDeepSeekWebResponseTransform(payload, adapterContext, config) {
|
|
153
|
-
if (!payload || typeof payload !== 'object') {
|
|
154
|
-
emitCompatError(new Error('[deepseek-web] invalid compat payload: expected object'), adapterContext, payload, {
|
|
155
|
-
reason: 'payload is not an object'
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
return callDeepSeekWebResponseCompat(buildCompatInput(payload, adapterContext, config), adapterContext).payload;
|
|
159
|
-
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { AdapterContext } from '../../hub/types/chat-envelope.js';
|
|
2
2
|
import type { JsonObject } from '../../hub/types/json.js';
|
|
3
|
+
import type { NativeReqOutboundStage3CompatInput } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
4
|
+
export declare function buildGeminiCliCompatInput(payload: JsonObject, adapterContext?: AdapterContext): NativeReqOutboundStage3CompatInput;
|
|
3
5
|
export declare function wrapGeminiCliRequest(payload: JsonObject, adapterContext?: AdapterContext): JsonObject;
|
|
@@ -12,7 +12,7 @@ function buildGeminiCliCompatContext(adapterContext) {
|
|
|
12
12
|
entryEndpoint: nativeContext.entryEndpoint ?? adapterContext?.entryEndpoint ?? DEFAULT_ENTRY_ENDPOINT
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
function buildGeminiCliCompatInput(payload, adapterContext) {
|
|
15
|
+
export function buildGeminiCliCompatInput(payload, adapterContext) {
|
|
16
16
|
return {
|
|
17
17
|
payload,
|
|
18
18
|
adapterContext: buildGeminiCliCompatContext(adapterContext),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGlmRequestCompatInput } from './glm-native-compat.js';
|
|
1
3
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2
4
|
function shouldDropInlineImagePart(part) {
|
|
3
5
|
const rawType = typeof part.type === 'string' ? part.type.toLowerCase() : '';
|
|
@@ -48,41 +50,5 @@ export function applyGlmHistoryImageTrim(payload) {
|
|
|
48
50
|
if (lastUserIdx === -1) {
|
|
49
51
|
return root;
|
|
50
52
|
}
|
|
51
|
-
|
|
52
|
-
for (let i = 0; i < messages.length; i += 1) {
|
|
53
|
-
const msg = messages[i];
|
|
54
|
-
const role = typeof msg.role === 'string' ? msg.role.toLowerCase() : '';
|
|
55
|
-
if (i < lastUserIdx && role === 'user') {
|
|
56
|
-
const contentValue = msg.content;
|
|
57
|
-
if (!Array.isArray(contentValue)) {
|
|
58
|
-
nextMessages.push(msg);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const newContent = [];
|
|
62
|
-
for (const part of contentValue) {
|
|
63
|
-
if (!isRecord(part)) {
|
|
64
|
-
newContent.push(part);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (shouldDropInlineImagePart(part)) {
|
|
68
|
-
// 丢弃历史中的 data:image/* 片段
|
|
69
|
-
// eslint-disable-next-line no-continue
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
newContent.push(part);
|
|
73
|
-
}
|
|
74
|
-
if (!newContent.length) {
|
|
75
|
-
// 历史消息只剩下 inline image 时,直接移除整条消息。
|
|
76
|
-
// 避免向 GLM 发送纯图片历史导致 1210。
|
|
77
|
-
// eslint-disable-next-line no-continue
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const cloned = { ...msg, content: newContent };
|
|
81
|
-
nextMessages.push(cloned);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
nextMessages.push(msg);
|
|
85
|
-
}
|
|
86
|
-
root.messages = nextMessages;
|
|
87
|
-
return root;
|
|
53
|
+
return runReqOutboundStage3CompatWithNative(buildGlmRequestCompatInput(payload)).payload;
|
|
88
54
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGlmRequestCompatInput } from './glm-native-compat.js';
|
|
1
3
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2
4
|
function normalizeImagePart(part) {
|
|
3
5
|
const rawType = typeof part.type === 'string' ? part.type.toLowerCase() : '';
|
|
@@ -48,36 +50,5 @@ export function applyGlmImageContentTransform(payload) {
|
|
|
48
50
|
if (!Array.isArray(messagesValue)) {
|
|
49
51
|
return root;
|
|
50
52
|
}
|
|
51
|
-
|
|
52
|
-
for (const msg of messagesValue) {
|
|
53
|
-
if (!isRecord(msg)) {
|
|
54
|
-
messages.push(msg);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const contentValue = msg.content;
|
|
58
|
-
if (!Array.isArray(contentValue)) {
|
|
59
|
-
messages.push(msg);
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const newContent = [];
|
|
63
|
-
for (const part of contentValue) {
|
|
64
|
-
if (!isRecord(part)) {
|
|
65
|
-
newContent.push(part);
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const normalizedImage = normalizeImagePart(part);
|
|
69
|
-
if (normalizedImage) {
|
|
70
|
-
newContent.push(normalizedImage);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
newContent.push(part);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
messages.push({
|
|
77
|
-
...msg,
|
|
78
|
-
content: newContent
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
root.messages = messages;
|
|
82
|
-
return root;
|
|
53
|
+
return runReqOutboundStage3CompatWithNative(buildGlmRequestCompatInput(payload)).payload;
|
|
83
54
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AdapterContext } from '../../hub/types/chat-envelope.js';
|
|
2
|
+
import type { JsonObject } from '../../hub/types/json.js';
|
|
3
|
+
import type { NativeReqOutboundCompatAdapterContextInput, NativeReqOutboundStage3CompatInput, NativeRespInboundStage3CompatInput } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
4
|
+
export declare function buildGlmCompatContext(adapterContext?: AdapterContext): NativeReqOutboundCompatAdapterContextInput;
|
|
5
|
+
export declare function buildGlmRequestCompatInput(payload: JsonObject, adapterContext?: AdapterContext): NativeReqOutboundStage3CompatInput;
|
|
6
|
+
export declare function buildGlmResponseCompatInput(payload: JsonObject, adapterContext?: AdapterContext): NativeRespInboundStage3CompatInput;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { buildNativeReqOutboundCompatAdapterContext } from '../../hub/pipeline/compat/native-adapter-context.js';
|
|
2
|
+
const PROFILE = 'chat:glm';
|
|
3
|
+
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
4
|
+
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
5
|
+
export function buildGlmCompatContext(adapterContext) {
|
|
6
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
7
|
+
const adapterProfile = adapterContext && typeof adapterContext['compatibilityProfile'] === 'string'
|
|
8
|
+
? String(adapterContext['compatibilityProfile']).trim() || undefined
|
|
9
|
+
: undefined;
|
|
10
|
+
return {
|
|
11
|
+
...nativeContext,
|
|
12
|
+
compatibilityProfile: nativeContext.compatibilityProfile ?? adapterProfile ?? PROFILE,
|
|
13
|
+
providerProtocol: nativeContext.providerProtocol ??
|
|
14
|
+
adapterContext?.providerProtocol ??
|
|
15
|
+
DEFAULT_PROVIDER_PROTOCOL,
|
|
16
|
+
entryEndpoint: nativeContext.entryEndpoint ??
|
|
17
|
+
adapterContext?.entryEndpoint ??
|
|
18
|
+
DEFAULT_ENTRY_ENDPOINT
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function buildGlmRequestCompatInput(payload, adapterContext) {
|
|
22
|
+
return {
|
|
23
|
+
payload,
|
|
24
|
+
adapterContext: buildGlmCompatContext(adapterContext),
|
|
25
|
+
explicitProfile: PROFILE
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function buildGlmResponseCompatInput(payload, adapterContext) {
|
|
29
|
+
return {
|
|
30
|
+
payload,
|
|
31
|
+
adapterContext: buildGlmCompatContext(adapterContext),
|
|
32
|
+
explicitProfile: PROFILE
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGlmRequestCompatInput } from './glm-native-compat.js';
|
|
1
3
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2
4
|
function extractImageUrlFromPart(part) {
|
|
3
5
|
const imageUrlBlock = isRecord(part.image_url)
|
|
@@ -98,80 +100,5 @@ export function applyGlmVisionPromptTransform(payload) {
|
|
|
98
100
|
if (!latestUserWithImage || !imageUrl) {
|
|
99
101
|
return root;
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
'\n' +
|
|
103
|
-
'输出必须是**单个合法 JSON 对象**,不要包含 Markdown、代码块标记或多余文本。请严格遵循下面的结构(字段可以为空数组,但必须存在):\n' +
|
|
104
|
-
'{\n' +
|
|
105
|
-
' "summary": "用 1-3 句整体描述这张图片(例如页面类型、主要区域、核心信息)",\n' +
|
|
106
|
-
' "marks": [\n' +
|
|
107
|
-
' {\n' +
|
|
108
|
-
' "type": "circle | arrow | underline | box | other",\n' +
|
|
109
|
-
' "color": "red | green | blue | yellow | other",\n' +
|
|
110
|
-
' "bbox": [x, y, width, height],\n' +
|
|
111
|
-
' "description": "该标记所圈出/指向/强调的内容,包含相关文字或 UI 元素描述"\n' +
|
|
112
|
-
' }\n' +
|
|
113
|
-
' ],\n' +
|
|
114
|
-
' "regions": [\n' +
|
|
115
|
-
' {\n' +
|
|
116
|
-
' "bbox": [x, y, width, height],\n' +
|
|
117
|
-
' "description": "该区域的可见内容(控件/图标/布局,以及其中出现的所有清晰可辨的文字)",\n' +
|
|
118
|
-
' "is_marked": true | false\n' +
|
|
119
|
-
' }\n' +
|
|
120
|
-
' ],\n' +
|
|
121
|
-
' "metadata": {\n' +
|
|
122
|
-
' "image_size_hint": "如果能推断出大致分辨率,请给出类似 1920x1080 的字符串;无法判断时用 null",\n' +
|
|
123
|
-
' "screenshot": true\n' +
|
|
124
|
-
' }\n' +
|
|
125
|
-
'}\n' +
|
|
126
|
-
'\n' +
|
|
127
|
-
'细则:\n' +
|
|
128
|
-
'1. 文字提取要求:\n' +
|
|
129
|
-
' - 如果图片中存在清晰可辨的文字(包括标题、菜单、按钮、标签、提示信息、弹窗、错误信息等),必须在对应的 regions.description 中**完整抄写**这些文字,按自然阅读顺序组织,避免遗漏。\n' +
|
|
130
|
-
' - 如果有多行文字,可以用换行符分隔,但仍放在同一个 description 字段中。\n' +
|
|
131
|
-
' - 对确实无法看清的文字,用类似 "(模糊,无法辨认)" 标注即可;没有任何文字也视为正常情况,此时只需描述界面结构。\n' +
|
|
132
|
-
'2. 标记识别(marks):\n' +
|
|
133
|
-
' - 对所有明显的圈选、箭头、下划线、高亮框等标记,必须在 marks 中列出,每一项提供大致 bbox、颜色和简短说明,说明其强调或指向的内容。\n' +
|
|
134
|
-
'3. 区域划分(regions):\n' +
|
|
135
|
-
' - 将截图拆分为若干有意义的区域:如导航栏、侧边栏、主内容区、弹窗、对话框、表格、代码块、表单等。\n' +
|
|
136
|
-
' - 每个区域的 description 中,既要描述布局/控件类型,也要包含该区域内的全部清晰文字内容。\n' +
|
|
137
|
-
' - is_marked 为 true 表示该区域与某个标记(marks)相关或被标记强调。\n' +
|
|
138
|
-
'4. 坐标规范:所有 bbox 使用相对于当前图片的像素坐标,左上角为 (0,0),width/height 为正数近似值。\n' +
|
|
139
|
-
'5. 无论图片内容如何,最终回答必须是合法 JSON,不能在 JSON 前后添加任何额外文本。';
|
|
140
|
-
const systemMessage = {
|
|
141
|
-
role: 'system',
|
|
142
|
-
content: systemContent
|
|
143
|
-
};
|
|
144
|
-
const originalUserText = collectUserTextFromMessage(latestUserWithImage);
|
|
145
|
-
const userBlocks = [];
|
|
146
|
-
if (originalUserText && originalUserText.trim().length) {
|
|
147
|
-
userBlocks.push({
|
|
148
|
-
type: 'text',
|
|
149
|
-
text: originalUserText.trim()
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
userBlocks.push({
|
|
154
|
-
type: 'text',
|
|
155
|
-
text: '请按照上面的 JSON 结构,详细描述这张图片的内容和标记。'
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
userBlocks.push({
|
|
159
|
-
type: 'image_url',
|
|
160
|
-
image_url: {
|
|
161
|
-
url: imageUrl
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
const userMessage = {
|
|
165
|
-
role: 'user',
|
|
166
|
-
content: userBlocks
|
|
167
|
-
};
|
|
168
|
-
// 丢弃原有 messages,仅保留新的 system + user。
|
|
169
|
-
root.messages = [systemMessage, userMessage];
|
|
170
|
-
// 对于专用视觉模型,限制 max_tokens,避免过大的 completion 预算进一步触发上下文相关错误。
|
|
171
|
-
const maxTokensValue = root.max_tokens;
|
|
172
|
-
if (typeof maxTokensValue === 'number' && Number.isFinite(maxTokensValue)) {
|
|
173
|
-
// 将超大值收敛到一个相对安全的上限;真实上限由上游再校验。
|
|
174
|
-
root.max_tokens = Math.min(maxTokensValue, 4096);
|
|
175
|
-
}
|
|
176
|
-
return root;
|
|
103
|
+
return runReqOutboundStage3CompatWithNative(buildGlmRequestCompatInput(payload)).payload;
|
|
177
104
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildGlmRequestCompatInput } from './glm-native-compat.js';
|
|
1
3
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2
4
|
const DEBUG_GLM_WEB_SEARCH = (process.env.ROUTECODEX_DEBUG_GLM_WEB_SEARCH || '').trim() === '1';
|
|
3
5
|
export function applyGlmWebSearchRequestTransform(payload) {
|
|
@@ -6,52 +8,17 @@ export function applyGlmWebSearchRequestTransform(payload) {
|
|
|
6
8
|
if (!isRecord(webSearchRaw)) {
|
|
7
9
|
return root;
|
|
8
10
|
}
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
let count;
|
|
16
|
-
if (typeof countValue === 'number' && Number.isFinite(countValue)) {
|
|
17
|
-
const normalized = Math.floor(countValue);
|
|
18
|
-
if (normalized >= 1 && normalized <= 50) {
|
|
19
|
-
count = normalized;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
if (!query) {
|
|
23
|
-
// No meaningful search query, drop the helper object and passthrough.
|
|
24
|
-
delete root.web_search;
|
|
25
|
-
return root;
|
|
26
|
-
}
|
|
27
|
-
const webSearchConfig = {
|
|
28
|
-
// 按 GLM 文档要求:search_engine 为必填,默认使用 search_std。
|
|
29
|
-
search_engine: 'search_std',
|
|
30
|
-
enable: true,
|
|
31
|
-
search_query: query,
|
|
32
|
-
// 返回搜索结果详情,便于我们在响应中提取摘要或调试。
|
|
33
|
-
search_result: true
|
|
34
|
-
};
|
|
35
|
-
if (recency) {
|
|
36
|
-
webSearchConfig.search_recency_filter = recency;
|
|
37
|
-
}
|
|
38
|
-
if (typeof count === 'number') {
|
|
39
|
-
webSearchConfig.count = count;
|
|
40
|
-
}
|
|
41
|
-
// 根据 OpenAPI:tools.anyOf[*] = WebSearchToolSchema[],即一次只能选择一种工具类型。
|
|
42
|
-
// 在 web_search 路由下,我们只保留 WebSearchToolSchema,丢弃其它 function/retrieval/MCP 工具,
|
|
43
|
-
// 避免混用导致后端忽略 web_search 配置。
|
|
44
|
-
const baseTool = {
|
|
45
|
-
type: 'web_search',
|
|
46
|
-
web_search: webSearchConfig
|
|
47
|
-
};
|
|
48
|
-
root.tools = [baseTool];
|
|
49
|
-
delete root.web_search;
|
|
11
|
+
const query = typeof webSearchRaw.query === 'string' ? webSearchRaw.query.trim() : '';
|
|
12
|
+
const recency = typeof webSearchRaw.recency === 'string' ? webSearchRaw.recency.trim() : undefined;
|
|
13
|
+
const count = typeof webSearchRaw.count === 'number' && Number.isFinite(webSearchRaw.count)
|
|
14
|
+
? Math.floor(webSearchRaw.count)
|
|
15
|
+
: undefined;
|
|
16
|
+
const normalized = runReqOutboundStage3CompatWithNative(buildGlmRequestCompatInput(payload)).payload;
|
|
50
17
|
if (DEBUG_GLM_WEB_SEARCH) {
|
|
51
18
|
try {
|
|
52
19
|
// eslint-disable-next-line no-console
|
|
53
20
|
console.log('\x1b[38;5;27m[compat][glm_web_search_request] applied web_search transform ' +
|
|
54
|
-
`search_engine
|
|
21
|
+
`search_engine=search_std ` +
|
|
55
22
|
`query=${JSON.stringify(query).slice(0, 200)} ` +
|
|
56
23
|
`count=${String(count ?? '')}\x1b[0m`);
|
|
57
24
|
}
|
|
@@ -59,5 +26,5 @@ export function applyGlmWebSearchRequestTransform(payload) {
|
|
|
59
26
|
// logging best-effort
|
|
60
27
|
}
|
|
61
28
|
}
|
|
62
|
-
return
|
|
29
|
+
return normalized;
|
|
63
30
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
2
|
+
import { buildIflowRequestCompatInput } from './iflow-native-compat.js';
|
|
1
3
|
function isRecord(value) {
|
|
2
4
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
5
|
}
|
|
@@ -8,46 +10,6 @@ function isKimiK25Model(value) {
|
|
|
8
10
|
const model = normalizeModel(value);
|
|
9
11
|
return model === 'kimi-k2.5' || model.startsWith('kimi-k2.5-');
|
|
10
12
|
}
|
|
11
|
-
function mirrorMaxTokens(record) {
|
|
12
|
-
const maxTokens = record.max_tokens;
|
|
13
|
-
if (typeof maxTokens === 'number' && Number.isFinite(maxTokens) && maxTokens > 0) {
|
|
14
|
-
record.max_new_tokens = Math.floor(maxTokens);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function isThinkingExplicitlyDisabled(value) {
|
|
18
|
-
if (value === false) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
if (!isRecord(value)) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
const enabled = value.enabled;
|
|
25
|
-
if (enabled === false) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
const type = typeof value.type === 'string' ? value.type.trim().toLowerCase() : '';
|
|
29
|
-
return type === 'disabled' || type === 'off';
|
|
30
|
-
}
|
|
31
|
-
function normalizeThinkingForKimi(record) {
|
|
32
|
-
const current = record.thinking;
|
|
33
|
-
if (current === undefined || current === null) {
|
|
34
|
-
record.thinking = { type: 'enabled' };
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
if (current === true) {
|
|
38
|
-
record.thinking = { type: 'enabled' };
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
if (isThinkingExplicitlyDisabled(current)) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
if (isRecord(current)) {
|
|
45
|
-
if (typeof current.type !== 'string' || current.type.trim().length === 0) {
|
|
46
|
-
current.type = 'enabled';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
13
|
/**
|
|
52
14
|
* iFlow/Kimi request defaults aligned with iflow-cli:
|
|
53
15
|
* - thinking enabled path: temperature=1
|
|
@@ -58,21 +20,10 @@ function normalizeThinkingForKimi(record) {
|
|
|
58
20
|
*/
|
|
59
21
|
export function applyIflowKimiCliDefaults(payload) {
|
|
60
22
|
try {
|
|
61
|
-
if (!isRecord(payload)) {
|
|
23
|
+
if (!isRecord(payload) || !isKimiK25Model(payload.model)) {
|
|
62
24
|
return payload;
|
|
63
25
|
}
|
|
64
|
-
|
|
65
|
-
if (!isKimiK25Model(root.model)) {
|
|
66
|
-
return root;
|
|
67
|
-
}
|
|
68
|
-
const thinkingEnabled = normalizeThinkingForKimi(root);
|
|
69
|
-
root.temperature = thinkingEnabled ? 1 : 0.6;
|
|
70
|
-
root.top_p = 0.95;
|
|
71
|
-
root.n = 1;
|
|
72
|
-
root.presence_penalty = 0;
|
|
73
|
-
root.frequency_penalty = 0;
|
|
74
|
-
mirrorMaxTokens(root);
|
|
75
|
-
return root;
|
|
26
|
+
return runReqOutboundStage3CompatWithNative(buildIflowRequestCompatInput(payload)).payload;
|
|
76
27
|
}
|
|
77
28
|
catch {
|
|
78
29
|
return payload;
|