@ducci/jarvis 1.0.7 → 1.0.8
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 +1 -1
- package/src/server/agent.js +40 -11
- package/src/server/app.js +10 -0
- package/src/server/logging.js +11 -0
package/package.json
CHANGED
package/src/server/agent.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadSystemPrompt, resolveSystemPrompt } from './config.js';
|
|
|
4
4
|
import { loadSession, saveSession, createSession } from './sessions.js';
|
|
5
5
|
import { loadTools, getToolDefinitions, executeTool } from './tools.js';
|
|
6
6
|
import { appendLog } from './logging.js';
|
|
7
|
+
import chalk from 'chalk';
|
|
7
8
|
|
|
8
9
|
const WRAP_UP_NOTE = `[System: You have reached the iteration limit. This is your final response for this run.
|
|
9
10
|
Respond with your normal JSON, but add a checkpoint field:
|
|
@@ -27,17 +28,33 @@ async function callModel(client, model, messages, tools) {
|
|
|
27
28
|
return await client.chat.completions.create(params);
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
function extractApiError(err, model) {
|
|
32
|
+
return {
|
|
33
|
+
model,
|
|
34
|
+
httpStatus: err?.status ?? null,
|
|
35
|
+
message: err?.message ?? String(err),
|
|
36
|
+
body: err?.error ?? null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
async function callModelWithFallback(client, config, messages, tools) {
|
|
41
|
+
let primaryErr = null;
|
|
31
42
|
try {
|
|
32
43
|
return await callModel(client, config.selectedModel, messages, tools);
|
|
33
|
-
} catch (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
} catch (err) {
|
|
45
|
+
primaryErr = err;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return await callModel(client, config.fallbackModel, messages, tools);
|
|
49
|
+
} catch (fallbackErr) {
|
|
50
|
+
const combined = new Error(
|
|
51
|
+
`Both primary (${config.selectedModel}) and fallback (${config.fallbackModel}) models failed. Last error: ${fallbackErr.message}`
|
|
52
|
+
);
|
|
53
|
+
combined.apiErrors = {
|
|
54
|
+
primary: extractApiError(primaryErr, config.selectedModel),
|
|
55
|
+
fallback: extractApiError(fallbackErr, config.fallbackModel),
|
|
56
|
+
};
|
|
57
|
+
throw combined;
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
|
|
@@ -57,8 +74,9 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
|
|
|
57
74
|
iteration++;
|
|
58
75
|
|
|
59
76
|
let modelResult;
|
|
77
|
+
const preparedMessages = prepareMessages(session.messages);
|
|
60
78
|
try {
|
|
61
|
-
modelResult = await callModelWithFallback(client, config,
|
|
79
|
+
modelResult = await callModelWithFallback(client, config, preparedMessages, toolDefs);
|
|
62
80
|
} catch (e) {
|
|
63
81
|
return {
|
|
64
82
|
iteration,
|
|
@@ -67,6 +85,8 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
|
|
|
67
85
|
status: 'model_error',
|
|
68
86
|
runToolCalls,
|
|
69
87
|
checkpoint: null,
|
|
88
|
+
errorDetail: e.apiErrors ?? { message: e.message },
|
|
89
|
+
contextInfo: { messageCount: preparedMessages.length },
|
|
70
90
|
};
|
|
71
91
|
}
|
|
72
92
|
|
|
@@ -123,6 +143,7 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
|
|
|
123
143
|
response = content;
|
|
124
144
|
logSummary = 'Model returned non-JSON final response.';
|
|
125
145
|
status = 'format_error';
|
|
146
|
+
return { iteration, response, logSummary, status, runToolCalls, checkpoint: null, rawResponse: content };
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
done = true;
|
|
@@ -147,6 +168,8 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
|
|
|
147
168
|
status: 'model_error',
|
|
148
169
|
runToolCalls,
|
|
149
170
|
checkpoint: null,
|
|
171
|
+
errorDetail: e.apiErrors ?? { message: e.message },
|
|
172
|
+
contextInfo: { messageCount: wrapUpMessages.length },
|
|
150
173
|
};
|
|
151
174
|
}
|
|
152
175
|
|
|
@@ -230,7 +253,7 @@ export async function handleChat(config, requestSessionId, userMessage) {
|
|
|
230
253
|
finalLogSummary = run.logSummary;
|
|
231
254
|
finalStatus = run.status;
|
|
232
255
|
|
|
233
|
-
|
|
256
|
+
const logEntry = {
|
|
234
257
|
iteration: run.iteration,
|
|
235
258
|
model: config.selectedModel,
|
|
236
259
|
userInput: userMessage,
|
|
@@ -238,7 +261,11 @@ export async function handleChat(config, requestSessionId, userMessage) {
|
|
|
238
261
|
response: finalResponse,
|
|
239
262
|
logSummary: finalLogSummary,
|
|
240
263
|
status: finalStatus,
|
|
241
|
-
}
|
|
264
|
+
};
|
|
265
|
+
if (run.errorDetail) logEntry.errorDetail = run.errorDetail;
|
|
266
|
+
if (run.contextInfo) logEntry.contextInfo = run.contextInfo;
|
|
267
|
+
if (run.rawResponse) logEntry.rawResponse = run.rawResponse;
|
|
268
|
+
appendLog(sessionId, logEntry);
|
|
242
269
|
break;
|
|
243
270
|
}
|
|
244
271
|
|
|
@@ -278,6 +305,8 @@ export async function handleChat(config, requestSessionId, userMessage) {
|
|
|
278
305
|
|
|
279
306
|
saveSession(sessionId, session);
|
|
280
307
|
|
|
308
|
+
console.log(`${chalk.magenta('<<<')} ${chalk.bold('Final Response')} [SID: ${chalk.dim(sessionId.slice(0, 8))}] ${chalk.italic(finalLogSummary)}`);
|
|
309
|
+
|
|
281
310
|
return {
|
|
282
311
|
sessionId,
|
|
283
312
|
response: finalResponse,
|
package/src/server/app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
import { realpathSync } from 'fs';
|
|
5
6
|
import { loadConfig, ensureDirectories } from './config.js';
|
|
@@ -12,6 +13,15 @@ const __dirname = path.dirname(__filename);
|
|
|
12
13
|
const app = express();
|
|
13
14
|
app.use(express.json());
|
|
14
15
|
|
|
16
|
+
// Request logger
|
|
17
|
+
app.use((req, res, next) => {
|
|
18
|
+
if (req.path === '/api/chat' && req.method === 'POST') {
|
|
19
|
+
const sid = req.body.sessionId || 'new';
|
|
20
|
+
console.log(`\n${chalk.magenta('>>>')} ${chalk.bold('Incoming Chat')} [SID: ${chalk.dim(sid.slice(0, 8))}]`);
|
|
21
|
+
}
|
|
22
|
+
next();
|
|
23
|
+
});
|
|
24
|
+
|
|
15
25
|
// Serve the built UI as static files
|
|
16
26
|
const uiDist = path.join(__dirname, '..', '..', 'ui', 'dist');
|
|
17
27
|
app.use(express.static(uiDist));
|
package/src/server/logging.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
3
4
|
import { PATHS } from './config.js';
|
|
4
5
|
|
|
5
6
|
export function appendLog(sessionId, entry) {
|
|
6
7
|
const logFile = path.join(PATHS.logsDir, `session-${sessionId}.jsonl`);
|
|
7
8
|
const line = JSON.stringify({ ts: new Date().toISOString(), sessionId, ...entry }) + '\n';
|
|
8
9
|
fs.appendFileSync(logFile, line, 'utf8');
|
|
10
|
+
|
|
11
|
+
// Console output for better visibility
|
|
12
|
+
const statusColor = entry.status === 'ok' ? chalk.green : chalk.red;
|
|
13
|
+
console.log(
|
|
14
|
+
`[${chalk.dim(new Date().toLocaleTimeString())}] ` +
|
|
15
|
+
`${chalk.blue('Session')}: ${chalk.dim(sessionId.slice(0, 8))} | ` +
|
|
16
|
+
`${chalk.yellow('Iter')}: ${entry.iteration} | ` +
|
|
17
|
+
`${chalk.cyan('Status')}: ${statusColor(entry.status)} | ` +
|
|
18
|
+
`${entry.logSummary || '(no summary)'}`
|
|
19
|
+
);
|
|
9
20
|
}
|