@zhongqian97-code/ecode 0.5.4 → 0.5.6
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/index.js +65 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -116,6 +116,7 @@ import { Box as Box6, useInput as useInput2, useStdout, useStdin } from "ink";
|
|
|
116
116
|
// src/providers/openai.ts
|
|
117
117
|
import OpenAI from "openai";
|
|
118
118
|
function createOpenAIProvider(profile) {
|
|
119
|
+
const THINK_END = "</think>";
|
|
119
120
|
const openai = new OpenAI({
|
|
120
121
|
baseURL: profile.baseUrl,
|
|
121
122
|
apiKey: profile.apiKey
|
|
@@ -149,14 +150,21 @@ function createOpenAIProvider(profile) {
|
|
|
149
150
|
return {
|
|
150
151
|
[Symbol.asyncIterator]: async function* () {
|
|
151
152
|
let thinkPhase = "pre";
|
|
152
|
-
let
|
|
153
|
+
let scanningForClose = false;
|
|
154
|
+
let closeSearchBuffer = "";
|
|
155
|
+
let visibleTextTail = "";
|
|
156
|
+
function rememberVisibleText(text) {
|
|
157
|
+
if (!text) return;
|
|
158
|
+
visibleTextTail = (visibleTextTail + text).slice(-256);
|
|
159
|
+
}
|
|
160
|
+
function hasNearbyVisibleOpenThink(maxDistance = 4) {
|
|
161
|
+
const openIdx = visibleTextTail.lastIndexOf("<think>");
|
|
162
|
+
const closeIdx = visibleTextTail.lastIndexOf(THINK_END);
|
|
163
|
+
if (openIdx === -1 || openIdx < closeIdx) return false;
|
|
164
|
+
return visibleTextTail.length - (openIdx + "<think>".length) <= maxDistance;
|
|
165
|
+
}
|
|
153
166
|
function processContent(raw) {
|
|
154
167
|
if (!raw) return { text: "", thinking: "" };
|
|
155
|
-
if (afterReasoningDetails) {
|
|
156
|
-
afterReasoningDetails = false;
|
|
157
|
-
thinkPhase = "post";
|
|
158
|
-
return { text: raw.startsWith("</think>") ? raw.slice(8) : raw, thinking: "" };
|
|
159
|
-
}
|
|
160
168
|
if (thinkPhase === "post") return { text: raw, thinking: "" };
|
|
161
169
|
if (thinkPhase === "pre") {
|
|
162
170
|
if (raw.startsWith("<think>")) {
|
|
@@ -167,11 +175,11 @@ function createOpenAIProvider(profile) {
|
|
|
167
175
|
return { text: raw, thinking: "" };
|
|
168
176
|
}
|
|
169
177
|
}
|
|
170
|
-
const endIdx = raw.indexOf(
|
|
178
|
+
const endIdx = raw.indexOf(THINK_END);
|
|
171
179
|
if (endIdx === -1) return { text: "", thinking: raw };
|
|
172
180
|
const thinking = raw.slice(0, endIdx);
|
|
173
181
|
thinkPhase = "post";
|
|
174
|
-
return { text: raw.slice(endIdx +
|
|
182
|
+
return { text: raw.slice(endIdx + THINK_END.length), thinking };
|
|
175
183
|
}
|
|
176
184
|
const requestParams = {
|
|
177
185
|
model: profile.model,
|
|
@@ -197,7 +205,7 @@ function createOpenAIProvider(profile) {
|
|
|
197
205
|
reasoningAccumulator += delta.reasoning_content;
|
|
198
206
|
}
|
|
199
207
|
if (delta.reasoning_details && delta.reasoning_details.length > 0) {
|
|
200
|
-
|
|
208
|
+
scanningForClose = true;
|
|
201
209
|
for (const rd of delta.reasoning_details) {
|
|
202
210
|
const id = rd.id ?? "";
|
|
203
211
|
const text = rd.text ?? "";
|
|
@@ -233,8 +241,36 @@ function createOpenAIProvider(profile) {
|
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
243
|
const isLast = choice.finish_reason != null;
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
let rawToProcess;
|
|
245
|
+
if (delta.reasoning_details && delta.reasoning_details.length > 0) {
|
|
246
|
+
rawToProcess = "";
|
|
247
|
+
} else if (scanningForClose) {
|
|
248
|
+
closeSearchBuffer += delta.content ?? "";
|
|
249
|
+
const closeIdx = closeSearchBuffer.indexOf(THINK_END);
|
|
250
|
+
if (closeIdx !== -1) {
|
|
251
|
+
const openIdx = closeSearchBuffer.lastIndexOf("<think>", closeIdx);
|
|
252
|
+
const hasNearbyLiteralPair = openIdx !== -1 && closeIdx - (openIdx + "<think>".length) <= 8;
|
|
253
|
+
const afterClose = closeSearchBuffer.slice(closeIdx + THINK_END.length);
|
|
254
|
+
rawToProcess = hasNearbyLiteralPair ? closeSearchBuffer : afterClose.length > 0 ? afterClose : closeSearchBuffer.slice(0, closeIdx);
|
|
255
|
+
scanningForClose = false;
|
|
256
|
+
closeSearchBuffer = "";
|
|
257
|
+
thinkPhase = "post";
|
|
258
|
+
} else if (isLast) {
|
|
259
|
+
rawToProcess = closeSearchBuffer;
|
|
260
|
+
scanningForClose = false;
|
|
261
|
+
closeSearchBuffer = "";
|
|
262
|
+
thinkPhase = "post";
|
|
263
|
+
} else {
|
|
264
|
+
rawToProcess = "";
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
rawToProcess = delta.content ?? "";
|
|
268
|
+
}
|
|
269
|
+
if (thinkPhase === "post" && rawToProcess.endsWith(THINK_END) && !rawToProcess.includes("<think>") && !hasNearbyVisibleOpenThink()) {
|
|
270
|
+
rawToProcess = rawToProcess.slice(0, -THINK_END.length);
|
|
271
|
+
}
|
|
272
|
+
const { text: filteredText, thinking: thinkContent } = processContent(rawToProcess);
|
|
273
|
+
rememberVisibleText(filteredText);
|
|
238
274
|
if (thinkContent) {
|
|
239
275
|
reasoningAccumulator += thinkContent;
|
|
240
276
|
}
|
|
@@ -3522,6 +3558,7 @@ var SkillRegistry = class {
|
|
|
3522
3558
|
};
|
|
3523
3559
|
|
|
3524
3560
|
// src/pipe.ts
|
|
3561
|
+
var PIPE_SYSTEM_PROMPT = "You are a helpful assistant running in headless pipe mode. You have access to read-only file tools (read, glob, grep). Answer concisely and accurately. Follow the user's requested language, format, and length exactly. Do not reveal chain-of-thought or emit raw <think> / </think> tags in the final answer; if you must refer to them, describe them in plain language instead.";
|
|
3525
3562
|
var PIPE_TOOLS = [READ_TOOL, GLOB_TOOL, GREP_TOOL];
|
|
3526
3563
|
function emit(out, event) {
|
|
3527
3564
|
out.write(JSON.stringify(event) + "\n");
|
|
@@ -3541,15 +3578,21 @@ async function executeToolCall(name, args) {
|
|
|
3541
3578
|
}
|
|
3542
3579
|
return `Unknown tool: ${name}`;
|
|
3543
3580
|
}
|
|
3544
|
-
async function runPipe(prompt, llm, out = process.stdout) {
|
|
3545
|
-
const messages = [
|
|
3581
|
+
async function runPipe(prompt, llm, out = process.stdout, systemPrompt) {
|
|
3582
|
+
const messages = [
|
|
3583
|
+
{ role: "system", content: systemPrompt ?? PIPE_SYSTEM_PROMPT },
|
|
3584
|
+
{ role: "user", content: prompt }
|
|
3585
|
+
];
|
|
3546
3586
|
while (true) {
|
|
3547
3587
|
let assistantText = "";
|
|
3588
|
+
const pendingTextChunks = [];
|
|
3548
3589
|
let lastUsage;
|
|
3590
|
+
let lastReasoning;
|
|
3591
|
+
let lastReasoningDetails;
|
|
3549
3592
|
const toolCalls = [];
|
|
3550
3593
|
for await (const chunk of llm.stream(messages, PIPE_TOOLS)) {
|
|
3551
3594
|
if (chunk.text) {
|
|
3552
|
-
|
|
3595
|
+
pendingTextChunks.push(chunk.text);
|
|
3553
3596
|
assistantText += chunk.text;
|
|
3554
3597
|
}
|
|
3555
3598
|
if (chunk.reasoning) {
|
|
@@ -3558,6 +3601,8 @@ async function runPipe(prompt, llm, out = process.stdout) {
|
|
|
3558
3601
|
if (chunk.done) {
|
|
3559
3602
|
if (chunk.toolCalls) toolCalls.push(...chunk.toolCalls);
|
|
3560
3603
|
if (chunk.usage) lastUsage = chunk.usage;
|
|
3604
|
+
if (chunk.reasoning) lastReasoning = chunk.reasoning;
|
|
3605
|
+
if (chunk.reasoningDetails) lastReasoningDetails = chunk.reasoningDetails;
|
|
3561
3606
|
}
|
|
3562
3607
|
}
|
|
3563
3608
|
if (toolCalls.length > 0) {
|
|
@@ -3568,7 +3613,9 @@ async function runPipe(prompt, llm, out = process.stdout) {
|
|
|
3568
3613
|
id: tc.id,
|
|
3569
3614
|
type: "function",
|
|
3570
3615
|
function: { name: tc.name, arguments: tc.arguments }
|
|
3571
|
-
}))
|
|
3616
|
+
})),
|
|
3617
|
+
...lastReasoning ? { reasoning_content: lastReasoning } : {},
|
|
3618
|
+
...lastReasoningDetails ? { reasoning_details: lastReasoningDetails } : {}
|
|
3572
3619
|
});
|
|
3573
3620
|
for (const tc of toolCalls) {
|
|
3574
3621
|
emit(out, { type: "tool_call", id: tc.id, name: tc.name, arguments: tc.arguments });
|
|
@@ -3577,6 +3624,9 @@ async function runPipe(prompt, llm, out = process.stdout) {
|
|
|
3577
3624
|
messages.push({ role: "tool", tool_call_id: tc.id, content });
|
|
3578
3625
|
}
|
|
3579
3626
|
} else {
|
|
3627
|
+
for (const text of pendingTextChunks) {
|
|
3628
|
+
emit(out, { type: "chunk", text });
|
|
3629
|
+
}
|
|
3580
3630
|
const doneEvent = lastUsage ? { type: "done", usage: lastUsage } : { type: "done" };
|
|
3581
3631
|
emit(out, doneEvent);
|
|
3582
3632
|
break;
|