@zhongqian97-code/ecode 0.5.36 → 0.5.38
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.
|
@@ -202,9 +202,229 @@ function createOpenAIProvider(profile) {
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// src/providers/anthropic.ts
|
|
206
|
+
import https from "https";
|
|
207
|
+
import { URL as URL2 } from "url";
|
|
208
|
+
function convertMessagesToAnthropic(messages) {
|
|
209
|
+
let system = "";
|
|
210
|
+
const anthropicMessages = [];
|
|
211
|
+
for (let i = 0; i < messages.length; i++) {
|
|
212
|
+
const msg = messages[i];
|
|
213
|
+
if (msg.role === "system") {
|
|
214
|
+
system = msg.content;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (msg.role === "user") {
|
|
218
|
+
anthropicMessages.push({ role: "user", content: msg.content });
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (msg.role === "tool") {
|
|
222
|
+
const toolResults = [];
|
|
223
|
+
while (i < messages.length && messages[i].role === "tool") {
|
|
224
|
+
const toolMsg = messages[i];
|
|
225
|
+
toolResults.push({
|
|
226
|
+
type: "tool_result",
|
|
227
|
+
tool_use_id: toolMsg.tool_call_id,
|
|
228
|
+
content: toolMsg.content
|
|
229
|
+
});
|
|
230
|
+
i++;
|
|
231
|
+
}
|
|
232
|
+
i--;
|
|
233
|
+
anthropicMessages.push({ role: "user", content: toolResults });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (msg.role === "assistant") {
|
|
237
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
238
|
+
const content = [];
|
|
239
|
+
if (msg.content) content.push({ type: "text", text: msg.content });
|
|
240
|
+
for (const tc of msg.tool_calls) {
|
|
241
|
+
let input = {};
|
|
242
|
+
try {
|
|
243
|
+
input = JSON.parse(tc.function.arguments);
|
|
244
|
+
} catch {
|
|
245
|
+
input = {};
|
|
246
|
+
}
|
|
247
|
+
content.push({ type: "tool_use", id: tc.id, name: tc.function.name, input });
|
|
248
|
+
}
|
|
249
|
+
anthropicMessages.push({ role: "assistant", content });
|
|
250
|
+
} else {
|
|
251
|
+
anthropicMessages.push({ role: "assistant", content: msg.content ?? "" });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return { system, anthropicMessages };
|
|
256
|
+
}
|
|
257
|
+
function convertToolsToAnthropic(tools) {
|
|
258
|
+
return tools.map((tool) => ({
|
|
259
|
+
name: tool.function.name,
|
|
260
|
+
description: tool.function.description,
|
|
261
|
+
input_schema: tool.function.parameters
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
function createAnthropicProvider(profile) {
|
|
265
|
+
const capabilities = Object.freeze(
|
|
266
|
+
Object.defineProperties({}, {
|
|
267
|
+
supportsTools: { value: true, writable: false, enumerable: true },
|
|
268
|
+
supportsReasoningStream: { value: false, writable: false, enumerable: true },
|
|
269
|
+
supportsImages: { value: true, writable: false, enumerable: true },
|
|
270
|
+
supportsJsonSchema: { value: true, writable: false, enumerable: true }
|
|
271
|
+
})
|
|
272
|
+
);
|
|
273
|
+
return {
|
|
274
|
+
capabilities,
|
|
275
|
+
stream(messages, tools, signal) {
|
|
276
|
+
return streamAnthropicResponse(profile, messages, tools, signal);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
async function* streamAnthropicResponse(profile, messages, tools, signal) {
|
|
281
|
+
const { system, anthropicMessages } = convertMessagesToAnthropic(messages);
|
|
282
|
+
const endpointUrl = new URL2(`${profile.baseUrl.replace(/\/$/, "")}/v1/messages`);
|
|
283
|
+
const requestBody = {
|
|
284
|
+
model: profile.model,
|
|
285
|
+
max_tokens: 8192,
|
|
286
|
+
messages: anthropicMessages,
|
|
287
|
+
stream: true
|
|
288
|
+
};
|
|
289
|
+
if (system) requestBody.system = system;
|
|
290
|
+
if (tools && tools.length > 0) requestBody.tools = convertToolsToAnthropic(tools);
|
|
291
|
+
const bodyStr = JSON.stringify(requestBody);
|
|
292
|
+
yield* makeHttpsStream(endpointUrl, profile.apiKey, bodyStr, signal);
|
|
293
|
+
}
|
|
294
|
+
async function* makeHttpsStream(url, apiKey, body, signal) {
|
|
295
|
+
var _a;
|
|
296
|
+
const queue = [];
|
|
297
|
+
let notify = null;
|
|
298
|
+
function push(item) {
|
|
299
|
+
queue.push(item);
|
|
300
|
+
if (notify) {
|
|
301
|
+
const fn = notify;
|
|
302
|
+
notify = null;
|
|
303
|
+
fn();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const port = url.port ? parseInt(url.port, 10) : 443;
|
|
307
|
+
const req = https.request(
|
|
308
|
+
{
|
|
309
|
+
hostname: url.hostname,
|
|
310
|
+
port,
|
|
311
|
+
path: url.pathname + url.search,
|
|
312
|
+
method: "POST",
|
|
313
|
+
headers: {
|
|
314
|
+
"x-api-key": apiKey,
|
|
315
|
+
"anthropic-version": "2023-06-01",
|
|
316
|
+
"content-type": "application/json",
|
|
317
|
+
accept: "text/event-stream",
|
|
318
|
+
"content-length": String(Buffer.byteLength(body, "utf8"))
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
(res) => {
|
|
322
|
+
const statusCode = res.statusCode;
|
|
323
|
+
if (statusCode !== 200) {
|
|
324
|
+
push(new Error(`Anthropic API error: HTTP ${statusCode}`));
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const r = res;
|
|
328
|
+
r.on("data", (chunk) => push(chunk));
|
|
329
|
+
r.on("end", () => push(null));
|
|
330
|
+
r.on("error", (err) => push(err));
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
req.on("error", (err) => push(err));
|
|
334
|
+
if (signal) {
|
|
335
|
+
signal.addEventListener(
|
|
336
|
+
"abort",
|
|
337
|
+
() => {
|
|
338
|
+
req.destroy();
|
|
339
|
+
push(null);
|
|
340
|
+
},
|
|
341
|
+
{ once: true }
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
req.write(body);
|
|
345
|
+
req.end();
|
|
346
|
+
let buffer = "";
|
|
347
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
348
|
+
let stopReason = null;
|
|
349
|
+
let inputTokens = 0;
|
|
350
|
+
let outputTokens = 0;
|
|
351
|
+
while (true) {
|
|
352
|
+
let item;
|
|
353
|
+
if (queue.length > 0) {
|
|
354
|
+
item = queue.shift();
|
|
355
|
+
} else {
|
|
356
|
+
await new Promise((resolve4) => {
|
|
357
|
+
notify = resolve4;
|
|
358
|
+
});
|
|
359
|
+
item = queue.shift();
|
|
360
|
+
}
|
|
361
|
+
if (item === null) break;
|
|
362
|
+
if (item instanceof Error) throw item;
|
|
363
|
+
buffer += item.toString("utf8");
|
|
364
|
+
const lines = buffer.split("\n");
|
|
365
|
+
buffer = lines.pop() ?? "";
|
|
366
|
+
for (const line of lines) {
|
|
367
|
+
if (!line.startsWith("data: ")) continue;
|
|
368
|
+
const data = line.slice(6).trim();
|
|
369
|
+
if (data === "[DONE]") break;
|
|
370
|
+
let event;
|
|
371
|
+
try {
|
|
372
|
+
event = JSON.parse(data);
|
|
373
|
+
} catch {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const eventType = event.type;
|
|
377
|
+
if (eventType === "message_start") {
|
|
378
|
+
const msg = event.message;
|
|
379
|
+
if ((_a = msg == null ? void 0 : msg.usage) == null ? void 0 : _a.input_tokens) inputTokens = msg.usage.input_tokens;
|
|
380
|
+
} else if (eventType === "content_block_start") {
|
|
381
|
+
const block = event.content_block;
|
|
382
|
+
const index = event.index;
|
|
383
|
+
if ((block == null ? void 0 : block.type) === "tool_use" && block.id && block.name) {
|
|
384
|
+
toolBlocks.set(index, { id: block.id, name: block.name, jsonAccum: "" });
|
|
385
|
+
}
|
|
386
|
+
} else if (eventType === "content_block_delta") {
|
|
387
|
+
const delta = event.delta;
|
|
388
|
+
const index = event.index;
|
|
389
|
+
if ((delta == null ? void 0 : delta.type) === "text_delta" && delta.text) {
|
|
390
|
+
yield { text: delta.text, done: false };
|
|
391
|
+
} else if ((delta == null ? void 0 : delta.type) === "input_json_delta" && delta.partial_json) {
|
|
392
|
+
const tool = toolBlocks.get(index);
|
|
393
|
+
if (tool) tool.jsonAccum += delta.partial_json;
|
|
394
|
+
}
|
|
395
|
+
} else if (eventType === "message_delta") {
|
|
396
|
+
const delta = event.delta;
|
|
397
|
+
stopReason = (delta == null ? void 0 : delta.stop_reason) ?? null;
|
|
398
|
+
const usage = event.usage;
|
|
399
|
+
if (usage == null ? void 0 : usage.output_tokens) outputTokens = usage.output_tokens;
|
|
400
|
+
} else if (eventType === "message_stop") {
|
|
401
|
+
const toolCalls = [...toolBlocks.values()].map((t) => ({
|
|
402
|
+
id: t.id,
|
|
403
|
+
name: t.name,
|
|
404
|
+
arguments: t.jsonAccum
|
|
405
|
+
}));
|
|
406
|
+
yield {
|
|
407
|
+
done: true,
|
|
408
|
+
text: "",
|
|
409
|
+
finishReason: stopReason,
|
|
410
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
411
|
+
usage: {
|
|
412
|
+
promptTokens: inputTokens,
|
|
413
|
+
completionTokens: outputTokens,
|
|
414
|
+
totalTokens: inputTokens + outputTokens
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
yield { done: true, text: "", finishReason: stopReason };
|
|
422
|
+
}
|
|
423
|
+
|
|
205
424
|
// src/providers/index.ts
|
|
206
425
|
function createProvider(profile) {
|
|
207
|
-
|
|
426
|
+
const useAnthropic = profile.apiFormat === "anthropic" || profile.baseUrl.includes("anthropic");
|
|
427
|
+
return useAnthropic ? createAnthropicProvider(profile) : createOpenAIProvider(profile);
|
|
208
428
|
}
|
|
209
429
|
function resolveActiveProfile(config, providerName) {
|
|
210
430
|
var _a, _b;
|
|
@@ -646,7 +866,7 @@ async function grepFiles(params) {
|
|
|
646
866
|
|
|
647
867
|
// src/tools/web_fetch.ts
|
|
648
868
|
import * as http from "http";
|
|
649
|
-
import * as
|
|
869
|
+
import * as https2 from "https";
|
|
650
870
|
var WEB_FETCH_TOOL = {
|
|
651
871
|
type: "function",
|
|
652
872
|
function: {
|
|
@@ -807,7 +1027,7 @@ async function nodeHttpFetch(url, signal) {
|
|
|
807
1027
|
return new Promise((resolve4, reject) => {
|
|
808
1028
|
const parsed = new URL(url);
|
|
809
1029
|
const isHttps = parsed.protocol === "https:";
|
|
810
|
-
const lib = isHttps ?
|
|
1030
|
+
const lib = isHttps ? https2 : http;
|
|
811
1031
|
const onAbort = () => {
|
|
812
1032
|
req.destroy();
|
|
813
1033
|
const err = new Error("The user aborted a request.");
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
todo,
|
|
35
35
|
webFetch,
|
|
36
36
|
writeFile
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-7DHMJ6NS.js";
|
|
38
38
|
import {
|
|
39
39
|
createSessionMetadata,
|
|
40
40
|
generateTitle,
|
|
@@ -231,7 +231,10 @@ var EventBus = class {
|
|
|
231
231
|
/** 发布事件,同步调用所有当前订阅者 */
|
|
232
232
|
emit(event) {
|
|
233
233
|
for (const listener of this.listeners) {
|
|
234
|
-
|
|
234
|
+
try {
|
|
235
|
+
listener(event);
|
|
236
|
+
} catch {
|
|
237
|
+
}
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
240
|
/** 添加事件监听器,返回取消订阅函数 */
|
|
@@ -807,7 +810,7 @@ if (rawArgs[0] === "web") {
|
|
|
807
810
|
webAutoApprove = true;
|
|
808
811
|
}
|
|
809
812
|
}
|
|
810
|
-
const { buildServer, generateAccessToken } = await import("./web-
|
|
813
|
+
const { buildServer, generateAccessToken } = await import("./web-7WWKTWTB.js");
|
|
811
814
|
const token = generateAccessToken();
|
|
812
815
|
const manager = new SessionManager(finalConfig);
|
|
813
816
|
const server = await buildServer({
|
|
@@ -902,6 +905,6 @@ Node.js 16/18 \u8BF7\u4F7F\u7528 --web \u6216 --pipe \u6A21\u5F0F\u3002
|
|
|
902
905
|
);
|
|
903
906
|
process.exit(1);
|
|
904
907
|
}
|
|
905
|
-
const { App, React, render } = await import("./ui-
|
|
908
|
+
const { App, React, render } = await import("./ui-XL5IDLH5.js");
|
|
906
909
|
render(React.createElement(App, { config: finalConfig, version: VERSION, autoMode, registry, trustedSkillDirs, initialMessages }));
|
|
907
910
|
}
|
|
@@ -592,6 +592,7 @@ function generateAdminHtml(version) {
|
|
|
592
592
|
status: 'idle', // idle | thinking | tool_calling | awaiting_confirm
|
|
593
593
|
wsRetries: 0,
|
|
594
594
|
streamingMsgEl: null, // DOM element currently receiving delta tokens
|
|
595
|
+
streamingThinkEl: null, // DOM element currently receiving reasoning tokens
|
|
595
596
|
showTools: true,
|
|
596
597
|
skills: [], // cached skills from /api/skills
|
|
597
598
|
skillsLoaded: false,
|
|
@@ -771,6 +772,7 @@ function generateAdminHtml(version) {
|
|
|
771
772
|
'<div class="empty-chat">\u2190 \u52A0\u8F7D\u4F1A\u8BDD\u4E2D\u2026</div>';
|
|
772
773
|
state.messages = [];
|
|
773
774
|
state.streamingMsgEl = null;
|
|
775
|
+
state.streamingThinkEl = null;
|
|
774
776
|
}
|
|
775
777
|
|
|
776
778
|
async function loadMessages(sessionId) {
|
|
@@ -824,6 +826,7 @@ function generateAdminHtml(version) {
|
|
|
824
826
|
if (body) body.classList.remove('cursor-blink');
|
|
825
827
|
state.streamingMsgEl = null;
|
|
826
828
|
}
|
|
829
|
+
state.streamingThinkEl = null;
|
|
827
830
|
}
|
|
828
831
|
|
|
829
832
|
// \u2500\u2500 WebSocket \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -935,14 +938,20 @@ function generateAdminHtml(version) {
|
|
|
935
938
|
finalizeStreamingMsg();
|
|
936
939
|
setStatus('idle');
|
|
937
940
|
} else if (type === 'message.reasoning') {
|
|
941
|
+
const token = msg.reasoning || msg.text || '';
|
|
938
942
|
const msgsEl = document.getElementById('messages');
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
943
|
+
if (!state.streamingThinkEl) {
|
|
944
|
+
const placeholder = msgsEl.querySelector('.empty-chat');
|
|
945
|
+
if (placeholder) placeholder.remove();
|
|
946
|
+
const thinkEl = document.createElement('div');
|
|
947
|
+
thinkEl.className = 'thinking-block';
|
|
948
|
+
thinkEl.textContent = token;
|
|
949
|
+
if (!state.showTools) thinkEl.style.display = 'none';
|
|
950
|
+
msgsEl.appendChild(thinkEl);
|
|
951
|
+
state.streamingThinkEl = thinkEl;
|
|
952
|
+
} else {
|
|
953
|
+
state.streamingThinkEl.textContent += token;
|
|
954
|
+
}
|
|
946
955
|
msgsEl.scrollTop = msgsEl.scrollHeight;
|
|
947
956
|
} else if (type === 'session.error') {
|
|
948
957
|
finalizeStreamingMsg();
|