@hileeon/mcc 0.1.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/.claude/CLAUDE.md +204 -0
- package/.claude/agents/.gitkeep +0 -0
- package/.claude/settings.json +9 -0
- package/.claude/skills/.gitkeep +0 -0
- package/README.md +127 -0
- package/dist/accounts/instance-manager.d.ts +11 -0
- package/dist/accounts/instance-manager.d.ts.map +1 -0
- package/dist/accounts/instance-manager.js +89 -0
- package/dist/accounts/instance-manager.js.map +1 -0
- package/dist/accounts/shared-manager.d.ts +25 -0
- package/dist/accounts/shared-manager.d.ts.map +1 -0
- package/dist/accounts/shared-manager.js +186 -0
- package/dist/accounts/shared-manager.js.map +1 -0
- package/dist/accounts/store.d.ts +30 -0
- package/dist/accounts/store.d.ts.map +1 -0
- package/dist/accounts/store.js +128 -0
- package/dist/accounts/store.js.map +1 -0
- package/dist/core/model-router.d.ts +30 -0
- package/dist/core/model-router.d.ts.map +1 -0
- package/dist/core/model-router.js +64 -0
- package/dist/core/model-router.js.map +1 -0
- package/dist/dashboard-server.d.ts +5 -0
- package/dist/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard-server.js +387 -0
- package/dist/dashboard-server.js.map +1 -0
- package/dist/mcc.d.ts +8 -0
- package/dist/mcc.d.ts.map +1 -0
- package/dist/mcc.js +474 -0
- package/dist/mcc.js.map +1 -0
- package/dist/mcp/external-registry.d.ts +24 -0
- package/dist/mcp/external-registry.d.ts.map +1 -0
- package/dist/mcp/external-registry.js +99 -0
- package/dist/mcp/external-registry.js.map +1 -0
- package/dist/mcp/installer.d.ts +31 -0
- package/dist/mcp/installer.d.ts.map +1 -0
- package/dist/mcp/installer.js +273 -0
- package/dist/mcp/installer.js.map +1 -0
- package/dist/mcp/mcp-config.d.ts +86 -0
- package/dist/mcp/mcp-config.d.ts.map +1 -0
- package/dist/mcp/mcp-config.js +178 -0
- package/dist/mcp/mcp-config.js.map +1 -0
- package/dist/mcp/registry.d.ts +23 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +100 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/proxy/proxy-daemon.d.ts +27 -0
- package/dist/proxy/proxy-daemon.d.ts.map +1 -0
- package/dist/proxy/proxy-daemon.js +192 -0
- package/dist/proxy/proxy-daemon.js.map +1 -0
- package/dist/proxy/proxy-entry.d.ts +11 -0
- package/dist/proxy/proxy-entry.d.ts.map +1 -0
- package/dist/proxy/proxy-entry.js +74 -0
- package/dist/proxy/proxy-entry.js.map +1 -0
- package/dist/proxy/proxy-paths.d.ts +27 -0
- package/dist/proxy/proxy-paths.d.ts.map +1 -0
- package/dist/proxy/proxy-paths.js +125 -0
- package/dist/proxy/proxy-paths.js.map +1 -0
- package/dist/proxy/proxy-server.d.ts +20 -0
- package/dist/proxy/proxy-server.d.ts.map +1 -0
- package/dist/proxy/proxy-server.js +280 -0
- package/dist/proxy/proxy-server.js.map +1 -0
- package/dist/proxy/upstream-url.d.ts +7 -0
- package/dist/proxy/upstream-url.d.ts.map +1 -0
- package/dist/proxy/upstream-url.js +38 -0
- package/dist/proxy/upstream-url.js.map +1 -0
- package/dist/shared/logger.d.ts +23 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +184 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/provider-preset-catalog.d.ts +41 -0
- package/dist/shared/provider-preset-catalog.d.ts.map +1 -0
- package/dist/shared/provider-preset-catalog.js +299 -0
- package/dist/shared/provider-preset-catalog.js.map +1 -0
- package/docs/decisions.md +33 -0
- package/docs/lessons.md +8 -0
- package/docs/product.md +37 -0
- package/lib/mcp/mcc-image-analysis-server.cjs +454 -0
- package/lib/mcp/mcc-websearch-server.cjs +339 -0
- package/lib/mcp-hooks/image-analysis-runtime.cjs +510 -0
- package/lib/mcp-hooks/image-analyzer-transformer.cjs +526 -0
- package/lib/mcp-hooks/websearch-transformer.cjs +1421 -0
- package/lib/proxy/config/config-loader-facade.js +24 -0
- package/lib/proxy/glmt/delta-accumulator.js +363 -0
- package/lib/proxy/glmt/glmt-transformer.js +204 -0
- package/lib/proxy/glmt/index.js +41 -0
- package/lib/proxy/glmt/locale-enforcer.js +69 -0
- package/lib/proxy/glmt/pipeline/content-transformer.js +162 -0
- package/lib/proxy/glmt/pipeline/index.js +20 -0
- package/lib/proxy/glmt/pipeline/request-transformer.js +116 -0
- package/lib/proxy/glmt/pipeline/response-builder.js +205 -0
- package/lib/proxy/glmt/pipeline/stream-parser.js +234 -0
- package/lib/proxy/glmt/pipeline/tool-call-handler.js +78 -0
- package/lib/proxy/glmt/pipeline/types.js +6 -0
- package/lib/proxy/glmt/reasoning-enforcer.js +151 -0
- package/lib/proxy/glmt/sse-parser.js +102 -0
- package/lib/proxy/services/logging.js +13 -0
- package/lib/proxy/transformers/request-transformer.js +452 -0
- package/lib/proxy/transformers/sse-stream-transformer.js +199 -0
- package/lib/shared/logger.cjs +138 -0
- package/package.json +35 -0
- package/src/accounts/instance-manager.ts +58 -0
- package/src/accounts/shared-manager.ts +154 -0
- package/src/accounts/store.ts +111 -0
- package/src/core/model-router.ts +82 -0
- package/src/dashboard-server.ts +407 -0
- package/src/mcc.ts +474 -0
- package/src/mcp/external-registry.ts +73 -0
- package/src/mcp/installer.ts +258 -0
- package/src/mcp/mcp-config.ts +168 -0
- package/src/mcp/registry.ts +89 -0
- package/src/proxy/proxy-daemon.ts +184 -0
- package/src/proxy/proxy-entry.ts +63 -0
- package/src/proxy/proxy-paths.ts +97 -0
- package/src/proxy/proxy-server.ts +278 -0
- package/src/proxy/upstream-url.ts +38 -0
- package/src/shared/logger.ts +140 -0
- package/src/shared/provider-preset-catalog.ts +340 -0
- package/tsconfig.json +33 -0
- package/ui/.prettierrc +9 -0
- package/ui/index.html +12 -0
- package/ui/package.json +33 -0
- package/ui/postcss.config.js +6 -0
- package/ui/src/App.tsx +753 -0
- package/ui/src/components/ui/button.tsx +48 -0
- package/ui/src/components/ui/card.tsx +50 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/label.tsx +20 -0
- package/ui/src/components/ui/select.tsx +80 -0
- package/ui/src/components/ui/switch.tsx +26 -0
- package/ui/src/components/ui/tabs.tsx +52 -0
- package/ui/src/index.css +33 -0
- package/ui/src/lib/api.ts +185 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +10 -0
- package/ui/src/vite-env.d.ts +1 -0
- package/ui/tailwind.config.js +49 -0
- package/ui/tsconfig.json +25 -0
- package/ui/vite.config.ts +20 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* LocaleEnforcer - Force English output from GLM models
|
|
5
|
+
*
|
|
6
|
+
* Purpose: GLM models default to Chinese when prompts are ambiguous or contain Chinese context.
|
|
7
|
+
* This module always injects "MUST respond in English" instruction into system prompt or first user message.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const enforcer = new LocaleEnforcer();
|
|
11
|
+
* const modifiedMessages = enforcer.injectInstruction(messages);
|
|
12
|
+
*
|
|
13
|
+
* Strategy:
|
|
14
|
+
* 1. If system prompt exists: Prepend instruction
|
|
15
|
+
* 2. If no system prompt: Prepend to first user message
|
|
16
|
+
* 3. Preserve message structure (string vs array content)
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.LocaleEnforcer = void 0;
|
|
20
|
+
class LocaleEnforcer {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.instruction =
|
|
23
|
+
options.instruction ||
|
|
24
|
+
'CRITICAL: You MUST respond in English only, regardless of the input language or context. This is a strict requirement.';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Inject English instruction into messages
|
|
28
|
+
* @param messages - Messages array to modify
|
|
29
|
+
* @returns Modified messages array
|
|
30
|
+
*/
|
|
31
|
+
injectInstruction(messages) {
|
|
32
|
+
// Clone messages to avoid mutation
|
|
33
|
+
const modifiedMessages = JSON.parse(JSON.stringify(messages));
|
|
34
|
+
// Strategy 1: Inject into system prompt (preferred)
|
|
35
|
+
const systemIndex = modifiedMessages.findIndex((m) => m.role === 'system');
|
|
36
|
+
if (systemIndex >= 0) {
|
|
37
|
+
const systemMsg = modifiedMessages[systemIndex];
|
|
38
|
+
if (typeof systemMsg.content === 'string') {
|
|
39
|
+
systemMsg.content = `${this.instruction}\n\n${systemMsg.content}`;
|
|
40
|
+
}
|
|
41
|
+
else if (Array.isArray(systemMsg.content)) {
|
|
42
|
+
systemMsg.content.unshift({
|
|
43
|
+
type: 'text',
|
|
44
|
+
text: this.instruction,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return modifiedMessages;
|
|
48
|
+
}
|
|
49
|
+
// Strategy 2: Prepend to first user message
|
|
50
|
+
const userIndex = modifiedMessages.findIndex((m) => m.role === 'user');
|
|
51
|
+
if (userIndex >= 0) {
|
|
52
|
+
const userMsg = modifiedMessages[userIndex];
|
|
53
|
+
if (typeof userMsg.content === 'string') {
|
|
54
|
+
userMsg.content = `${this.instruction}\n\n${userMsg.content}`;
|
|
55
|
+
}
|
|
56
|
+
else if (Array.isArray(userMsg.content)) {
|
|
57
|
+
userMsg.content.unshift({
|
|
58
|
+
type: 'text',
|
|
59
|
+
text: this.instruction,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return modifiedMessages;
|
|
63
|
+
}
|
|
64
|
+
// No system or user messages found (edge case)
|
|
65
|
+
return modifiedMessages;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.LocaleEnforcer = LocaleEnforcer;
|
|
69
|
+
//# sourceMappingURL=locale-enforcer.js.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ContentTransformer - Handle message sanitization and content transformations
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Sanitize messages for OpenAI API compatibility
|
|
7
|
+
* - Extract thinking control tags from messages
|
|
8
|
+
* - Detect think keywords in user prompts
|
|
9
|
+
* - Transform tools between Anthropic and OpenAI formats
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ContentTransformer = void 0;
|
|
13
|
+
class ContentTransformer {
|
|
14
|
+
constructor(defaultThinking = true) {
|
|
15
|
+
this.defaultThinking = defaultThinking;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize messages for OpenAI API compatibility
|
|
19
|
+
*/
|
|
20
|
+
sanitizeMessages(messages) {
|
|
21
|
+
const result = [];
|
|
22
|
+
for (const msg of messages) {
|
|
23
|
+
// If content is a string, add as-is
|
|
24
|
+
if (typeof msg.content === 'string') {
|
|
25
|
+
result.push(msg);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// If content is an array, process blocks
|
|
29
|
+
if (Array.isArray(msg.content)) {
|
|
30
|
+
// Separate tool_result blocks from other content
|
|
31
|
+
const toolResults = msg.content.filter((block) => block.type === 'tool_result');
|
|
32
|
+
const textBlocks = msg.content.filter((block) => block.type === 'text');
|
|
33
|
+
// CRITICAL: Tool messages must come BEFORE user text in OpenAI API
|
|
34
|
+
for (const toolResult of toolResults) {
|
|
35
|
+
result.push({
|
|
36
|
+
role: 'tool',
|
|
37
|
+
content: typeof toolResult.content === 'string'
|
|
38
|
+
? toolResult.content
|
|
39
|
+
: JSON.stringify(toolResult.content),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Add text content as user/assistant message AFTER tool messages
|
|
43
|
+
if (textBlocks.length > 0) {
|
|
44
|
+
const textContent = textBlocks.length === 1
|
|
45
|
+
? textBlocks[0].text || ''
|
|
46
|
+
: textBlocks.map((b) => b.text || '').join('\n');
|
|
47
|
+
result.push({
|
|
48
|
+
role: msg.role,
|
|
49
|
+
content: textContent,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// If no content at all, add empty message
|
|
53
|
+
if (textBlocks.length === 0 && toolResults.length === 0) {
|
|
54
|
+
result.push({
|
|
55
|
+
role: msg.role,
|
|
56
|
+
content: '',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// Fallback: return message as-is
|
|
62
|
+
result.push(msg);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Transform Anthropic tools to OpenAI tools format
|
|
68
|
+
*/
|
|
69
|
+
transformTools(anthropicTools) {
|
|
70
|
+
return anthropicTools.map((tool) => ({
|
|
71
|
+
type: 'function',
|
|
72
|
+
function: {
|
|
73
|
+
name: tool.name,
|
|
74
|
+
description: tool.description,
|
|
75
|
+
parameters: tool.input_schema || {},
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if messages contain thinking control tags
|
|
81
|
+
*/
|
|
82
|
+
hasThinkingTags(messages) {
|
|
83
|
+
for (const msg of messages) {
|
|
84
|
+
if (msg.role !== 'user')
|
|
85
|
+
continue;
|
|
86
|
+
const content = msg.content;
|
|
87
|
+
if (typeof content !== 'string')
|
|
88
|
+
continue;
|
|
89
|
+
// Check for control tags
|
|
90
|
+
if (/<Thinking:(On|Off)>/i.test(content) || /<Effort:(Low|Medium|High)>/i.test(content)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract thinking control tags from user messages
|
|
98
|
+
*/
|
|
99
|
+
extractThinkingControl(messages) {
|
|
100
|
+
const config = {
|
|
101
|
+
thinking: this.defaultThinking,
|
|
102
|
+
effort: 'medium',
|
|
103
|
+
};
|
|
104
|
+
// Scan user messages for control tags
|
|
105
|
+
for (const msg of messages) {
|
|
106
|
+
if (msg.role !== 'user')
|
|
107
|
+
continue;
|
|
108
|
+
const content = msg.content;
|
|
109
|
+
if (typeof content !== 'string')
|
|
110
|
+
continue;
|
|
111
|
+
// Check for <Thinking:On|Off>
|
|
112
|
+
const thinkingMatch = content.match(/<Thinking:(On|Off)>/i);
|
|
113
|
+
if (thinkingMatch) {
|
|
114
|
+
config.thinking = thinkingMatch[1].toLowerCase() === 'on';
|
|
115
|
+
}
|
|
116
|
+
// Check for <Effort:Low|Medium|High>
|
|
117
|
+
const effortMatch = content.match(/<Effort:(Low|Medium|High)>/i);
|
|
118
|
+
if (effortMatch) {
|
|
119
|
+
config.effort = effortMatch[1].toLowerCase();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return config;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Detect Anthropic-style "think" keywords in user prompts
|
|
126
|
+
*/
|
|
127
|
+
detectThinkKeywords(messages) {
|
|
128
|
+
if (!messages || messages.length === 0)
|
|
129
|
+
return null;
|
|
130
|
+
// Extract text from user messages
|
|
131
|
+
const text = messages
|
|
132
|
+
.filter((m) => m.role === 'user')
|
|
133
|
+
.map((m) => {
|
|
134
|
+
if (typeof m.content === 'string')
|
|
135
|
+
return m.content;
|
|
136
|
+
if (Array.isArray(m.content)) {
|
|
137
|
+
return m.content
|
|
138
|
+
.filter((block) => block.type === 'text')
|
|
139
|
+
.map((block) => block.text || '')
|
|
140
|
+
.join(' ');
|
|
141
|
+
}
|
|
142
|
+
return '';
|
|
143
|
+
})
|
|
144
|
+
.join(' ');
|
|
145
|
+
// Priority: ultrathink > think harder > think hard > think
|
|
146
|
+
if (/\bultrathink\b/i.test(text)) {
|
|
147
|
+
return { thinking: true, effort: 'max', keyword: 'ultrathink' };
|
|
148
|
+
}
|
|
149
|
+
if (/\bthink\s+harder\b/i.test(text)) {
|
|
150
|
+
return { thinking: true, effort: 'high', keyword: 'think harder' };
|
|
151
|
+
}
|
|
152
|
+
if (/\bthink\s+hard\b/i.test(text)) {
|
|
153
|
+
return { thinking: true, effort: 'medium', keyword: 'think hard' };
|
|
154
|
+
}
|
|
155
|
+
if (/\bthink\b/i.test(text)) {
|
|
156
|
+
return { thinking: true, effort: 'low', keyword: 'think' };
|
|
157
|
+
}
|
|
158
|
+
return null; // No keywords detected
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.ContentTransformer = ContentTransformer;
|
|
162
|
+
//# sourceMappingURL=content-transformer.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pipeline Module Exports
|
|
4
|
+
*
|
|
5
|
+
* Barrel file for the GLMT transformation pipeline
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.RequestTransformer = exports.StreamParser = exports.ResponseBuilder = exports.ToolCallHandler = exports.ContentTransformer = void 0;
|
|
9
|
+
// Pipeline components
|
|
10
|
+
var content_transformer_1 = require("./content-transformer");
|
|
11
|
+
Object.defineProperty(exports, "ContentTransformer", { enumerable: true, get: function () { return content_transformer_1.ContentTransformer; } });
|
|
12
|
+
var tool_call_handler_1 = require("./tool-call-handler");
|
|
13
|
+
Object.defineProperty(exports, "ToolCallHandler", { enumerable: true, get: function () { return tool_call_handler_1.ToolCallHandler; } });
|
|
14
|
+
var response_builder_1 = require("./response-builder");
|
|
15
|
+
Object.defineProperty(exports, "ResponseBuilder", { enumerable: true, get: function () { return response_builder_1.ResponseBuilder; } });
|
|
16
|
+
var stream_parser_1 = require("./stream-parser");
|
|
17
|
+
Object.defineProperty(exports, "StreamParser", { enumerable: true, get: function () { return stream_parser_1.StreamParser; } });
|
|
18
|
+
var request_transformer_1 = require("./request-transformer");
|
|
19
|
+
Object.defineProperty(exports, "RequestTransformer", { enumerable: true, get: function () { return request_transformer_1.RequestTransformer; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RequestTransformer - Handle Anthropic → OpenAI request transformation
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Transform Anthropic request format to OpenAI format
|
|
7
|
+
* - Inject locale and reasoning instructions
|
|
8
|
+
* - Map models and configure parameters
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.RequestTransformer = void 0;
|
|
12
|
+
const locale_enforcer_1 = require("../locale-enforcer");
|
|
13
|
+
const reasoning_enforcer_1 = require("../reasoning-enforcer");
|
|
14
|
+
const content_transformer_1 = require("./content-transformer");
|
|
15
|
+
class RequestTransformer {
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
const defaultThinking = config.defaultThinking ?? true;
|
|
18
|
+
this.log = config.log || (() => { });
|
|
19
|
+
this.localeEnforcer = new locale_enforcer_1.LocaleEnforcer();
|
|
20
|
+
this.reasoningEnforcer = new reasoning_enforcer_1.ReasoningEnforcer({
|
|
21
|
+
enabled: config.explicitReasoning ?? true,
|
|
22
|
+
});
|
|
23
|
+
this.contentTransformer = new content_transformer_1.ContentTransformer(defaultThinking);
|
|
24
|
+
this.modelMaxTokens = {
|
|
25
|
+
'GLM-4.6': 128000,
|
|
26
|
+
'GLM-4.5': 96000,
|
|
27
|
+
'GLM-4.5-air': 16000,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Transform Anthropic request to OpenAI format
|
|
32
|
+
*/
|
|
33
|
+
transform(anthropicRequest) {
|
|
34
|
+
try {
|
|
35
|
+
const messages = anthropicRequest.messages || [];
|
|
36
|
+
// 1. Extract thinking control from messages
|
|
37
|
+
const thinkingConfig = this.contentTransformer.extractThinkingControl(messages);
|
|
38
|
+
const hasControlTags = this.contentTransformer.hasThinkingTags(messages);
|
|
39
|
+
// 2. Detect "think" keywords in user prompts
|
|
40
|
+
const keywordConfig = this.contentTransformer.detectThinkKeywords(messages);
|
|
41
|
+
if (keywordConfig && !anthropicRequest.thinking && !hasControlTags) {
|
|
42
|
+
thinkingConfig.thinking = keywordConfig.thinking;
|
|
43
|
+
thinkingConfig.effort = keywordConfig.effort;
|
|
44
|
+
this.log(`Detected think keyword: ${keywordConfig.keyword}, effort=${keywordConfig.effort}`);
|
|
45
|
+
}
|
|
46
|
+
// 3. Check anthropicRequest.thinking parameter (takes precedence)
|
|
47
|
+
if (anthropicRequest.thinking) {
|
|
48
|
+
if (anthropicRequest.thinking.type === 'enabled') {
|
|
49
|
+
thinkingConfig.thinking = true;
|
|
50
|
+
this.log('Claude CLI explicitly enabled thinking');
|
|
51
|
+
}
|
|
52
|
+
else if (anthropicRequest.thinking.type === 'disabled') {
|
|
53
|
+
thinkingConfig.thinking = false;
|
|
54
|
+
this.log('Claude CLI explicitly disabled thinking');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.log(`Final thinking control: ${JSON.stringify(thinkingConfig)}`);
|
|
58
|
+
// 4. Map model
|
|
59
|
+
const glmModel = this.mapModel(anthropicRequest.model);
|
|
60
|
+
// 5. Inject locale instruction
|
|
61
|
+
const messagesWithLocale = this.localeEnforcer.injectInstruction(messages);
|
|
62
|
+
// 6. Inject reasoning instruction
|
|
63
|
+
const messagesWithReasoning = this.reasoningEnforcer.injectInstruction(messagesWithLocale, thinkingConfig);
|
|
64
|
+
// 7. Build OpenAI request
|
|
65
|
+
const openaiRequest = {
|
|
66
|
+
model: glmModel,
|
|
67
|
+
messages: this.contentTransformer.sanitizeMessages(messagesWithReasoning),
|
|
68
|
+
max_tokens: this.getMaxTokens(glmModel),
|
|
69
|
+
stream: anthropicRequest.stream ?? false,
|
|
70
|
+
};
|
|
71
|
+
// 8. Transform tools if present
|
|
72
|
+
if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
|
|
73
|
+
openaiRequest.tools = this.contentTransformer.transformTools(anthropicRequest.tools);
|
|
74
|
+
openaiRequest.tool_choice = 'auto';
|
|
75
|
+
this.log(`Transformed ${anthropicRequest.tools.length} tools for OpenAI format`);
|
|
76
|
+
}
|
|
77
|
+
// 9. Preserve optional parameters
|
|
78
|
+
if (anthropicRequest.temperature !== undefined) {
|
|
79
|
+
openaiRequest.temperature = anthropicRequest.temperature;
|
|
80
|
+
}
|
|
81
|
+
if (anthropicRequest.top_p !== undefined) {
|
|
82
|
+
openaiRequest.top_p = anthropicRequest.top_p;
|
|
83
|
+
}
|
|
84
|
+
if (anthropicRequest.stream !== undefined) {
|
|
85
|
+
openaiRequest.stream = anthropicRequest.stream;
|
|
86
|
+
}
|
|
87
|
+
// 10. Inject reasoning parameters
|
|
88
|
+
this.injectReasoningParams(openaiRequest, thinkingConfig);
|
|
89
|
+
return { openaiRequest, thinkingConfig };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const err = error;
|
|
93
|
+
console.error('[RequestTransformer] Transformation error:', err);
|
|
94
|
+
return {
|
|
95
|
+
openaiRequest: anthropicRequest,
|
|
96
|
+
thinkingConfig: { thinking: false, effort: 'medium' },
|
|
97
|
+
error: err.message,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
injectReasoningParams(openaiRequest, thinkingConfig) {
|
|
102
|
+
openaiRequest.do_sample = true;
|
|
103
|
+
if (thinkingConfig.thinking) {
|
|
104
|
+
openaiRequest.reasoning = true;
|
|
105
|
+
openaiRequest.reasoning_effort = thinkingConfig.effort;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
mapModel(_anthropicModel) {
|
|
109
|
+
return 'GLM-4.6';
|
|
110
|
+
}
|
|
111
|
+
getMaxTokens(model) {
|
|
112
|
+
return this.modelMaxTokens[model] || 128000;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.RequestTransformer = RequestTransformer;
|
|
116
|
+
//# sourceMappingURL=request-transformer.js.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ResponseBuilder - Create SSE events for Anthropic streaming format
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Create message_start, message_delta, message_stop events
|
|
7
|
+
* - Create content_block_start, content_block_delta, content_block_stop events
|
|
8
|
+
* - Generate thinking signatures
|
|
9
|
+
* - Map stop reasons between formats
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.ResponseBuilder = void 0;
|
|
36
|
+
const crypto = __importStar(require("crypto"));
|
|
37
|
+
class ResponseBuilder {
|
|
38
|
+
constructor(verbose = false) {
|
|
39
|
+
this.verbose = verbose;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create message_start event
|
|
43
|
+
*/
|
|
44
|
+
createMessageStartEvent(accumulator) {
|
|
45
|
+
return {
|
|
46
|
+
event: 'message_start',
|
|
47
|
+
data: {
|
|
48
|
+
type: 'message_start',
|
|
49
|
+
message: {
|
|
50
|
+
id: accumulator.getMessageId(),
|
|
51
|
+
type: 'message',
|
|
52
|
+
role: accumulator.getRole(),
|
|
53
|
+
content: [],
|
|
54
|
+
model: accumulator.getModel() || 'glm-5',
|
|
55
|
+
stop_reason: null,
|
|
56
|
+
usage: {
|
|
57
|
+
input_tokens: accumulator.getInputTokens(),
|
|
58
|
+
output_tokens: 0,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create content_block_start event
|
|
66
|
+
*/
|
|
67
|
+
createContentBlockStartEvent(block) {
|
|
68
|
+
return {
|
|
69
|
+
event: 'content_block_start',
|
|
70
|
+
data: {
|
|
71
|
+
type: 'content_block_start',
|
|
72
|
+
index: block.index,
|
|
73
|
+
content_block: {
|
|
74
|
+
type: block.type,
|
|
75
|
+
[block.type]: '',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create thinking_delta event
|
|
82
|
+
*/
|
|
83
|
+
createThinkingDeltaEvent(block, delta) {
|
|
84
|
+
return {
|
|
85
|
+
event: 'content_block_delta',
|
|
86
|
+
data: {
|
|
87
|
+
type: 'content_block_delta',
|
|
88
|
+
index: block.index,
|
|
89
|
+
delta: {
|
|
90
|
+
type: 'thinking_delta',
|
|
91
|
+
thinking: delta,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create text_delta event
|
|
98
|
+
*/
|
|
99
|
+
createTextDeltaEvent(block, delta) {
|
|
100
|
+
return {
|
|
101
|
+
event: 'content_block_delta',
|
|
102
|
+
data: {
|
|
103
|
+
type: 'content_block_delta',
|
|
104
|
+
index: block.index,
|
|
105
|
+
delta: {
|
|
106
|
+
type: 'text_delta',
|
|
107
|
+
text: delta,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create thinking signature delta event
|
|
114
|
+
*/
|
|
115
|
+
createSignatureDeltaEvent(block) {
|
|
116
|
+
// FIX: Guard against empty content (signature timing race)
|
|
117
|
+
if (!block.content || block.content.length === 0) {
|
|
118
|
+
if (this.verbose) {
|
|
119
|
+
console.error(`[ResponseBuilder] WARNING: Skipping signature for empty thinking block ${block.index}`);
|
|
120
|
+
console.error(`This indicates a race condition - signature requested before content accumulated`);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const signature = this.generateThinkingSignature(block.content);
|
|
125
|
+
if (this.verbose) {
|
|
126
|
+
console.error(`[ResponseBuilder] Generating signature for block ${block.index}: ${block.content.length} chars`);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
event: 'content_block_delta',
|
|
130
|
+
data: {
|
|
131
|
+
type: 'content_block_delta',
|
|
132
|
+
index: block.index,
|
|
133
|
+
delta: {
|
|
134
|
+
type: 'thinking_signature_delta',
|
|
135
|
+
signature: signature,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create content_block_stop event
|
|
142
|
+
*/
|
|
143
|
+
createContentBlockStopEvent(block) {
|
|
144
|
+
return {
|
|
145
|
+
event: 'content_block_stop',
|
|
146
|
+
data: {
|
|
147
|
+
type: 'content_block_stop',
|
|
148
|
+
index: block.index,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generate finalization events (message_delta + message_stop)
|
|
154
|
+
*/
|
|
155
|
+
createFinalizationEvents(accumulator, stopReason) {
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
event: 'message_delta',
|
|
159
|
+
data: {
|
|
160
|
+
type: 'message_delta',
|
|
161
|
+
delta: {
|
|
162
|
+
stop_reason: stopReason,
|
|
163
|
+
},
|
|
164
|
+
usage: {
|
|
165
|
+
input_tokens: accumulator.getInputTokens(),
|
|
166
|
+
output_tokens: accumulator.getOutputTokens(),
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
event: 'message_stop',
|
|
172
|
+
data: {
|
|
173
|
+
type: 'message_stop',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Generate thinking signature for Claude Code UI
|
|
180
|
+
*/
|
|
181
|
+
generateThinkingSignature(thinking) {
|
|
182
|
+
// Generate signature hash
|
|
183
|
+
const hash = crypto.createHash('sha256').update(thinking).digest('hex').substring(0, 16);
|
|
184
|
+
return {
|
|
185
|
+
type: 'thinking_signature',
|
|
186
|
+
hash: hash,
|
|
187
|
+
length: thinking.length,
|
|
188
|
+
timestamp: Date.now(),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Map OpenAI stop reason to Anthropic stop reason
|
|
193
|
+
*/
|
|
194
|
+
mapStopReason(openaiReason) {
|
|
195
|
+
const mapping = {
|
|
196
|
+
stop: 'end_turn',
|
|
197
|
+
length: 'max_tokens',
|
|
198
|
+
tool_calls: 'tool_use',
|
|
199
|
+
content_filter: 'stop_sequence',
|
|
200
|
+
};
|
|
201
|
+
return mapping[openaiReason] || 'end_turn';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.ResponseBuilder = ResponseBuilder;
|
|
205
|
+
//# sourceMappingURL=response-builder.js.map
|