@zhongqian97-code/ecode 0.3.7 → 0.3.9

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.
Files changed (2) hide show
  1. package/dist/index.js +66 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38,7 +38,11 @@ var MODEL_CONTEXT_LIMITS = {
38
38
  "claude-haiku-4-5-20251001": 2e5,
39
39
  // DeepSeek 系列(上下文较小,截断策略需更激进)
40
40
  "deepseek-chat": 65536,
41
- "deepseek-reasoner": 65536
41
+ "deepseek-reasoner": 65536,
42
+ // MiniMax 系列(baseUrl: https://api.minimax.chat/v1)
43
+ "MiniMax-M2.5": 1e6,
44
+ "MiniMax-M2.5-highspeed": 192e3,
45
+ "MiniMax-Text-01": 1e6
42
46
  };
43
47
  var DEFAULT_CONTEXT_LIMIT = 128e3;
44
48
  function getContextLimit(model, override) {
@@ -144,6 +148,34 @@ function createOpenAIProvider(profile) {
144
148
  stream(messages, tools, signal) {
145
149
  return {
146
150
  [Symbol.asyncIterator]: async function* () {
151
+ let inThinkBlock = false;
152
+ function stripThinkTags(raw) {
153
+ let text = "";
154
+ let thinking = "";
155
+ let i = 0;
156
+ while (i < raw.length) {
157
+ if (!inThinkBlock) {
158
+ const start = raw.indexOf("<think>", i);
159
+ if (start === -1) {
160
+ text += raw.slice(i);
161
+ break;
162
+ }
163
+ text += raw.slice(i, start);
164
+ inThinkBlock = true;
165
+ i = start + 7;
166
+ } else {
167
+ const end = raw.indexOf("</think>", i);
168
+ if (end === -1) {
169
+ thinking += raw.slice(i);
170
+ break;
171
+ }
172
+ thinking += raw.slice(i, end);
173
+ inThinkBlock = false;
174
+ i = end + 8;
175
+ }
176
+ }
177
+ return { text, thinking };
178
+ }
147
179
  const requestParams = {
148
180
  model: profile.model,
149
181
  messages,
@@ -159,6 +191,7 @@ function createOpenAIProvider(profile) {
159
191
  );
160
192
  const tcAccumulator = /* @__PURE__ */ new Map();
161
193
  let reasoningAccumulator = "";
194
+ const reasoningDetailsAcc = /* @__PURE__ */ new Map();
162
195
  for await (const chunk of response) {
163
196
  const choice = chunk.choices[0];
164
197
  if (!choice) continue;
@@ -166,6 +199,26 @@ function createOpenAIProvider(profile) {
166
199
  if (delta.reasoning_content) {
167
200
  reasoningAccumulator += delta.reasoning_content;
168
201
  }
202
+ if (delta.reasoning_details && delta.reasoning_details.length > 0) {
203
+ for (const rd of delta.reasoning_details) {
204
+ const id = rd.id ?? "";
205
+ const text = rd.text ?? "";
206
+ if (!reasoningDetailsAcc.has(id)) {
207
+ reasoningDetailsAcc.set(id, {
208
+ type: rd.type ?? "reasoning.text",
209
+ id,
210
+ format: rd.format ?? "",
211
+ index: rd.index ?? 0,
212
+ text: ""
213
+ });
214
+ }
215
+ const existing = reasoningDetailsAcc.get(id);
216
+ existing.text += text;
217
+ if (!delta.reasoning_content && text) {
218
+ reasoningAccumulator += text;
219
+ }
220
+ }
221
+ }
169
222
  if (delta.tool_calls) {
170
223
  for (const tc of delta.tool_calls) {
171
224
  if (!tcAccumulator.has(tc.index)) {
@@ -181,17 +234,24 @@ function createOpenAIProvider(profile) {
181
234
  existing.arguments += tc.function?.arguments ?? "";
182
235
  }
183
236
  }
184
- const isLast = choice.finish_reason !== null;
237
+ const isLast = choice.finish_reason != null;
238
+ const raw = delta.content ?? "";
239
+ const { text: filteredText, thinking: thinkContent } = stripThinkTags(raw);
240
+ if (thinkContent) {
241
+ reasoningAccumulator += thinkContent;
242
+ }
185
243
  if (isLast) {
186
244
  const rawUsage = chunk.usage;
187
245
  yield {
188
- text: delta.content ?? "",
246
+ text: filteredText,
189
247
  done: true,
190
248
  finishReason: choice.finish_reason,
191
249
  // tcAccumulator 为空说明本轮没有工具调用,传 undefined 而非空数组,
192
250
  // 让调用方用 if (chunk.toolCalls) 做简洁判断
193
251
  toolCalls: tcAccumulator.size > 0 ? Array.from(tcAccumulator.values()) : void 0,
194
252
  reasoning: reasoningAccumulator || void 0,
253
+ // reasoningDetails 仅在流中出现过结构化推理时返回(MiniMax 兼容)
254
+ reasoningDetails: reasoningDetailsAcc.size > 0 ? Array.from(reasoningDetailsAcc.values()) : void 0,
195
255
  // 将 snake_case 的原始字段映射为 camelCase,对外接口保持一致
196
256
  usage: rawUsage ? {
197
257
  promptTokens: rawUsage.prompt_tokens,
@@ -200,9 +260,10 @@ function createOpenAIProvider(profile) {
200
260
  } : void 0
201
261
  };
202
262
  } else {
263
+ const incrementalReasoning = delta.reasoning_content || thinkContent || (delta.reasoning_details && delta.reasoning_details.length > 0 ? delta.reasoning_details.map((rd) => rd.text ?? "").join("") : void 0) || void 0;
203
264
  yield {
204
- text: delta.content ?? "",
205
- reasoning: delta.reasoning_content,
265
+ text: filteredText,
266
+ reasoning: incrementalReasoning || void 0,
206
267
  done: false
207
268
  };
208
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",