netheriteai-code 0.3.5 → 0.3.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "netheriteai-code",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "NetheriteAI:Code by hurdacu. High-performance coding assistant.",
5
5
  "author": "hurdacu",
6
6
  "type": "module",
package/src/agent.js CHANGED
@@ -160,6 +160,14 @@ export async function runAgentTurn({ workspaceRoot, model, history, userPrompt,
160
160
  }
161
161
  messages.push(assistantMessage);
162
162
 
163
+ // Keep history lean to avoid server overload
164
+ if (messages.length > 15) {
165
+ const systemMsg = messages[0];
166
+ const recent = messages.slice(-10);
167
+ messages.length = 0;
168
+ messages.push(systemMsg, ...recent);
169
+ }
170
+
163
171
  if (singleFileTurn) {
164
172
  const generated = extractGeneratedCode(assistant.content || "");
165
173
  if (generated?.code) {
package/src/cli.js CHANGED
@@ -11,9 +11,12 @@ import { printTable, resolveWorkspaceRoot } from "./utils.js";
11
11
 
12
12
  const execFileAsync = promisify(execFile);
13
13
 
14
- const VERSION = "0.3.5";
14
+ const VERSION = "0.3.7";
15
15
 
16
16
  async function handleAutoUpdate() {
17
+ // If we were just restarted by an update, don't check again
18
+ if (process.argv.includes("--no-update")) return;
19
+
17
20
  try {
18
21
  const latest = execSync("npm view netheriteai-code version", {
19
22
  encoding: "utf8",
@@ -52,7 +55,8 @@ async function handleAutoUpdate() {
52
55
  process.stdout.write(`\u001b[${y};${dx}H\u001b[1m${doneMsg}\u001b[0m`);
53
56
  process.stdout.write("\u001b[?1049l");
54
57
 
55
- spawn(process.argv[0], process.argv.slice(1), { stdio: "inherit", detached: false });
58
+ // Pass the skip flag to the restarted process to prevent looping
59
+ spawn(process.argv[0], [...process.argv.slice(1), "--no-update"], { stdio: "inherit", detached: false });
56
60
  process.exit(0);
57
61
  }
58
62
  } catch {
package/src/ollama.js CHANGED
@@ -3,9 +3,7 @@ import { promisify } from "node:util";
3
3
 
4
4
  const execFileAsync = promisify(execFile);
5
5
  const BASE_URL = process.env.NETHERITE_BASE_URL || "http://176.88.249.119:11434";
6
- const PREFERRED_DEFAULT_MODELS = [
7
- "glm-5:cloud",
8
- ];
6
+ const PREFERRED_DEFAULT_MODELS = ["glm-5:cloud"];
9
7
 
10
8
  async function request(pathname, body, signal, retries = 3) {
11
9
  for (let i = 0; i < retries; i++) {
@@ -18,13 +16,10 @@ async function request(pathname, body, signal, retries = 3) {
18
16
  });
19
17
 
20
18
  if (response.ok) return response;
21
-
22
- // If 500 error, wait and retry
23
19
  if (response.status === 500 && i < retries - 1) {
24
20
  await new Promise(r => setTimeout(r, 1000));
25
21
  continue;
26
22
  }
27
-
28
23
  throw new Error("Server down");
29
24
  } catch (err) {
30
25
  if (err.name === "AbortError") throw err;
@@ -40,16 +35,14 @@ async function request(pathname, body, signal, retries = 3) {
40
35
  export async function listModels() {
41
36
  try {
42
37
  const response = await fetch(`${BASE_URL}/api/tags`);
43
- if (!response.ok) {
44
- throw new Error("Server down");
45
- }
38
+ if (!response.ok) throw new Error("Server down");
46
39
  const json = await response.json();
47
- return (json.models || []).map((model) => ({
48
- name: model.name,
49
- size: model.size,
50
- modifiedAt: model.modified_at,
51
- digest: model.digest,
52
- details: model.details || {},
40
+ return (json.models || []).map(m => ({
41
+ name: m.name,
42
+ size: m.size,
43
+ modifiedAt: m.modified_at,
44
+ digest: m.digest,
45
+ details: m.details || {},
53
46
  }));
54
47
  } catch {
55
48
  throw new Error("Server down");
@@ -59,13 +52,9 @@ export async function listModels() {
59
52
  export async function pickDefaultModel() {
60
53
  try {
61
54
  const models = await listModels();
62
- if (!models.length) {
63
- throw new Error("Server down");
64
- }
65
- const preferredMatch = models.find((model) =>
66
- PREFERRED_DEFAULT_MODELS.some((preferred) => preferred.toLowerCase() === model.name.toLowerCase()),
67
- );
68
- return preferredMatch ? preferredMatch.name : models[0].name;
55
+ if (!models.length) throw new Error("Server down");
56
+ const match = models.find(m => PREFERRED_DEFAULT_MODELS.some(p => p.toLowerCase() === m.name.toLowerCase()));
57
+ return match ? match.name : models[0].name;
69
58
  } catch {
70
59
  throw new Error("Server down");
71
60
  }
@@ -73,145 +62,81 @@ export async function pickDefaultModel() {
73
62
 
74
63
  export async function chat({ model, messages, tools, signal }) {
75
64
  const body = { model, messages, stream: false };
76
- if (tools && tools.length) body.tools = tools;
77
-
65
+ if (tools?.length && model !== "glm-5:cloud") body.tools = tools;
78
66
  const response = await request("/api/chat", body, signal);
79
67
  const json = await response.json();
80
- return {
81
- ...json.message,
82
- reasoning: json.message?.thinking || "",
83
- };
84
- }
85
-
86
- function createTaggedStreamParser({ onContent, onReasoning }) {
87
- let buffer = "";
88
- let inReasoning = false;
89
- const openTag = "<think>";
90
- const closeTag = "</think>";
91
-
92
- function emitVisible(text) { if (text) onContent?.(text); }
93
- function emitReasoning(text) { if (text) onReasoning?.(text); }
94
-
95
- function flush(final = false) {
96
- while (buffer.length) {
97
- if (inReasoning) {
98
- const closeIndex = buffer.indexOf(closeTag);
99
- if (closeIndex !== -1) {
100
- emitReasoning(buffer.slice(0, closeIndex));
101
- buffer = buffer.slice(closeIndex + closeTag.length);
102
- inReasoning = false;
103
- continue;
104
- }
105
- if (final) {
106
- emitReasoning(buffer);
107
- buffer = "";
108
- return;
109
- }
110
- if (buffer.length > closeTag.length) {
111
- emitReasoning(buffer.slice(0, buffer.length - closeTag.length));
112
- buffer = buffer.slice(buffer.length - closeTag.length);
113
- }
114
- return;
115
- }
116
-
117
- const openIndex = buffer.indexOf(openTag);
118
- if (openIndex !== -1) {
119
- emitVisible(buffer.slice(0, openIndex));
120
- buffer = buffer.slice(openIndex + openTag.length);
121
- inReasoning = true;
122
- continue;
123
- }
124
- if (final) {
125
- emitVisible(buffer);
126
- buffer = "";
127
- return;
128
- }
129
- if (buffer.length > openTag.length) {
130
- emitVisible(buffer.slice(0, buffer.length - openTag.length));
131
- buffer = buffer.slice(buffer.length - openTag.length);
132
- }
133
- return;
68
+ let content = json.message?.content || "";
69
+ let reasoning = json.message?.thinking || "";
70
+
71
+ if (content.includes("<think>")) {
72
+ const parts = content.split(/<think>|<\/think>/);
73
+ if (parts.length >= 3) {
74
+ reasoning = parts[1];
75
+ content = (parts[0] + parts[2]).trim();
134
76
  }
135
77
  }
136
-
137
- return {
138
- push(text) { buffer += text; flush(false); },
139
- finish() { flush(true); },
140
- };
78
+
79
+ return { ...json.message, content, reasoning };
141
80
  }
142
81
 
143
82
  export async function chatStream({ model, messages, tools, onChunk, onReasoningChunk, signal }) {
144
83
  const body = { model, messages, stream: true };
145
- if (tools && tools.length && model !== "glm-5:cloud") body.tools = tools;
84
+ if (tools?.length && model !== "glm-5:cloud") body.tools = tools;
146
85
 
147
86
  const response = await request("/api/chat", body, signal);
148
-
149
- if (!response.body) {
150
- throw new Error("Server down");
151
- }
87
+ if (!response.body) throw new Error("Server down");
152
88
 
153
89
  const decoder = new TextDecoder();
154
90
  let lineBuffer = "";
155
91
  let lastMessage = { role: "assistant", content: "", reasoning: "", tool_calls: [] };
156
- const parser = createTaggedStreamParser({
157
- onContent(text) {
158
- lastMessage.content += text;
159
- onChunk?.(text);
160
- },
161
- onReasoning(text) {
162
- lastMessage.reasoning += text;
163
- onReasoningChunk?.(text);
164
- },
165
- });
92
+ let inThinkTag = false;
166
93
 
167
94
  try {
168
- // Use async iterator for better Node.js compatibility
169
95
  for await (const chunk of response.body) {
170
96
  lineBuffer += decoder.decode(chunk, { stream: true });
171
-
172
97
  let lines = lineBuffer.split("\n");
173
98
  lineBuffer = lines.pop() || "";
174
-
99
+
175
100
  for (const line of lines) {
176
101
  if (!line.trim()) continue;
177
102
  try {
178
103
  const json = JSON.parse(line);
179
- const message = json.message || {};
180
- if (message.thinking) {
181
- lastMessage.reasoning += message.thinking;
182
- onReasoningChunk?.(message.thinking);
183
- }
184
- if (message.content) {
185
- parser.push(message.content);
104
+ const msg = json.message || {};
105
+
106
+ if (msg.thinking) {
107
+ lastMessage.reasoning += msg.thinking;
108
+ onReasoningChunk?.(msg.thinking);
186
109
  }
187
- if (message.tool_calls?.length) {
188
- lastMessage.tool_calls = message.tool_calls;
189
- }
190
- if (message.role) {
191
- lastMessage.role = message.role;
110
+
111
+ if (msg.content) {
112
+ let text = msg.content;
113
+ if (text.includes("<think>")) {
114
+ inThinkTag = true;
115
+ const [before, after] = text.split("<think>");
116
+ if (before) { lastMessage.content += before; onChunk?.(before); }
117
+ text = after || "";
118
+ }
119
+ if (inThinkTag && text.includes("</think>")) {
120
+ inThinkTag = false;
121
+ const [think, after] = text.split("</think>");
122
+ if (think) { lastMessage.reasoning += think; onReasoningChunk?.(think); }
123
+ text = after || "";
124
+ }
125
+
126
+ if (text) {
127
+ if (inThinkTag) {
128
+ lastMessage.reasoning += text;
129
+ onReasoningChunk?.(text);
130
+ } else {
131
+ lastMessage.content += text;
132
+ onChunk?.(text);
133
+ }
134
+ }
192
135
  }
193
- } catch {
194
- // Ignore parse errors from partial lines or server noise
195
- }
196
- }
197
- }
198
-
199
- if (lineBuffer.trim()) {
200
- try {
201
- const json = JSON.parse(lineBuffer);
202
- const message = json.message || {};
203
- if (message.thinking) {
204
- lastMessage.reasoning += message.thinking;
205
- onReasoningChunk?.(message.thinking);
206
- }
207
- if (message.content) {
208
- parser.push(message.content);
209
- }
210
- if (message.tool_calls?.length) {
211
- lastMessage.tool_calls = message.tool_calls;
212
- }
213
- } catch {
214
- // Final line noise
136
+
137
+ if (msg.tool_calls?.length) lastMessage.tool_calls = msg.tool_calls;
138
+ if (msg.role) lastMessage.role = msg.role;
139
+ } catch {}
215
140
  }
216
141
  }
217
142
  } catch (err) {
@@ -219,6 +144,5 @@ export async function chatStream({ model, messages, tools, onChunk, onReasoningC
219
144
  throw new Error("Server down");
220
145
  }
221
146
 
222
- parser.finish();
223
147
  return lastMessage;
224
148
  }