mo commited on
Commit ·
bf62c9f
1
Parent(s): d0f0aa1
feat(brain): auto smart/cheap mode — per-task brain profiles with provider routing (simple/standard/complex heuristics, quality-first vs cost-first key ordering)
Browse files- apps/desktop/src/agent.js +19 -8
- apps/desktop/src/cloud.js +2 -2
apps/desktop/src/agent.js
CHANGED
|
@@ -299,6 +299,7 @@ export class AgentDaemon extends EventEmitter {
|
|
| 299 |
if (Number.isFinite(+brain.temperature)) this.#brain.temperature = Math.min(1, Math.max(0, +brain.temperature));
|
| 300 |
if (typeof brain.extraRules === 'string') this.#brain.extraRules = brain.extraRules.slice(0, 2000);
|
| 301 |
if (typeof brain.verify === 'boolean') this.#brain.verify = brain.verify;
|
|
|
|
| 302 |
}
|
| 303 |
|
| 304 |
// ── Task execution (cloud reasoning + local tools) ──────────────
|
|
@@ -312,6 +313,7 @@ export class AgentDaemon extends EventEmitter {
|
|
| 312 |
messages,
|
| 313 |
tools: tools.list(),
|
| 314 |
temperature: opts.temperature ?? this.#brain.temperature,
|
|
|
|
| 315 |
onChunk: (delta) => {
|
| 316 |
this.#control.token(delta, runId);
|
| 317 |
this.emit('task:token', delta, runId);
|
|
@@ -372,6 +374,16 @@ export class AgentDaemon extends EventEmitter {
|
|
| 372 |
const steps = [];
|
| 373 |
let final = '';
|
| 374 |
const sngine = CLOUD.platform === 'sngine';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
const t0 = Date.now();
|
| 376 |
const usageTotals = { input: 0, output: 0, total: 0, reasoning: 0, cacheRead: 0, cacheCreation: 0 };
|
| 377 |
let lastModel = '';
|
|
@@ -418,16 +430,16 @@ export class AgentDaemon extends EventEmitter {
|
|
| 418 |
} else {
|
| 419 |
// Sngine platform: agentic loop — brain reasons deeply:
|
| 420 |
// plan → act (tools) → observe → reflect → answer → verify.
|
| 421 |
-
const systemPrompt = this.#toolsPrompt(cloudTask);
|
| 422 |
const messages = [
|
| 423 |
{ role: 'system', content: systemPrompt },
|
| 424 |
{ role: 'user', content: task },
|
| 425 |
];
|
| 426 |
|
| 427 |
let corrections = 0;
|
| 428 |
-
for (let i = 0; i <
|
| 429 |
const tThink = Date.now();
|
| 430 |
-
const thinkRes = await this.#thinkWithRetry(messages, runId);
|
| 431 |
const answer = thinkRes.text ?? thinkRes ?? '';
|
| 432 |
const thinkMs = Date.now() - tThink;
|
| 433 |
if (thinkRes.usage) addUsage(thinkRes.usage);
|
|
@@ -541,7 +553,7 @@ export class AgentDaemon extends EventEmitter {
|
|
| 541 |
messages.push({ role: 'user', content: 'Step limit reached. Reply {"reasoning":"brief summary of what you did","answer":"..."} — or plain text. No more tools.' });
|
| 542 |
try {
|
| 543 |
const tThink = Date.now();
|
| 544 |
-
const thinkRes = await this.#thinkWithRetry(messages, runId);
|
| 545 |
const r2 = parseBrainReply(thinkRes.text ?? '');
|
| 546 |
if (r2 && r2.kind === 'answer') final = r2.answer;
|
| 547 |
else if (r2 && r2.kind === 'text') final = r2.text;
|
|
@@ -564,12 +576,12 @@ export class AgentDaemon extends EventEmitter {
|
|
| 564 |
|
| 565 |
// Self-verification pass: the brain re-checks its own answer against
|
| 566 |
// the evidence before it reaches the user (fixes premature or sloppy answers).
|
| 567 |
-
if (final &&
|
| 568 |
try {
|
| 569 |
messages.push({ role: 'assistant', content: final });
|
| 570 |
messages.push({ role: 'user', content: 'VERIFY: You are about to send this answer to the user. Check it against the tool results above: is every claim factual, complete and direct? If something is wrong or missing, fix it. Reply {"reasoning":"what you checked","answer":"<corrected or unchanged answer>"}.' });
|
| 571 |
const tThink = Date.now();
|
| 572 |
-
const vRes = await this.#thinkWithRetry(messages, runId);
|
| 573 |
const vr = parseBrainReply(vRes.text ?? '');
|
| 574 |
if (vr && vr.kind === 'answer' && vr.answer.trim()) final = vr.answer;
|
| 575 |
else if (vr && vr.kind === 'text' && (vRes.text ?? '').trim()) final = vRes.text;
|
|
@@ -665,11 +677,10 @@ export class AgentDaemon extends EventEmitter {
|
|
| 665 |
}
|
| 666 |
}
|
| 667 |
|
| 668 |
-
#toolsPrompt(taskRow) {
|
| 669 |
const rows = tools.list()
|
| 670 |
.map((t) => `- ${t.name}: ${t.description}${t.args ? ` (args: ${JSON.stringify(t.args)})` : ''}`)
|
| 671 |
.join('\n');
|
| 672 |
-
const brain = this.#brain;
|
| 673 |
let p = `You are mona-agent — the AI agent controlling this device (${process.platform}). You reason deeply and act precisely: plan, act, observe, reflect, then answer.
|
| 674 |
|
| 675 |
## Reasoning protocol
|
|
|
|
| 299 |
if (Number.isFinite(+brain.temperature)) this.#brain.temperature = Math.min(1, Math.max(0, +brain.temperature));
|
| 300 |
if (typeof brain.extraRules === 'string') this.#brain.extraRules = brain.extraRules.slice(0, 2000);
|
| 301 |
if (typeof brain.verify === 'boolean') this.#brain.verify = brain.verify;
|
| 302 |
+
if (typeof brain.mode === 'string') this.#brain.mode = brain.mode;
|
| 303 |
}
|
| 304 |
|
| 305 |
// ── Task execution (cloud reasoning + local tools) ──────────────
|
|
|
|
| 313 |
messages,
|
| 314 |
tools: tools.list(),
|
| 315 |
temperature: opts.temperature ?? this.#brain.temperature,
|
| 316 |
+
profile: opts.profile ?? null,
|
| 317 |
onChunk: (delta) => {
|
| 318 |
this.#control.token(delta, runId);
|
| 319 |
this.emit('task:token', delta, runId);
|
|
|
|
| 374 |
const steps = [];
|
| 375 |
let final = '';
|
| 376 |
const sngine = CLOUD.platform === 'sngine';
|
| 377 |
+
// Per-task brain: the cloud decides mode-aware settings for each task
|
| 378 |
+
// (auto = best of smart & cheap, computed server-side per task).
|
| 379 |
+
const brain = {
|
| 380 |
+
maxSteps: this.#brain.maxSteps,
|
| 381 |
+
temperature: this.#brain.temperature,
|
| 382 |
+
verify: this.#brain.verify,
|
| 383 |
+
extraRules: this.#brain.extraRules,
|
| 384 |
+
profile: null,
|
| 385 |
+
...(cloudTask?.brain || {}),
|
| 386 |
+
};
|
| 387 |
const t0 = Date.now();
|
| 388 |
const usageTotals = { input: 0, output: 0, total: 0, reasoning: 0, cacheRead: 0, cacheCreation: 0 };
|
| 389 |
let lastModel = '';
|
|
|
|
| 430 |
} else {
|
| 431 |
// Sngine platform: agentic loop — brain reasons deeply:
|
| 432 |
// plan → act (tools) → observe → reflect → answer → verify.
|
| 433 |
+
const systemPrompt = this.#toolsPrompt(cloudTask, brain);
|
| 434 |
const messages = [
|
| 435 |
{ role: 'system', content: systemPrompt },
|
| 436 |
{ role: 'user', content: task },
|
| 437 |
];
|
| 438 |
|
| 439 |
let corrections = 0;
|
| 440 |
+
for (let i = 0; i < brain.maxSteps; i++) {
|
| 441 |
const tThink = Date.now();
|
| 442 |
+
const thinkRes = await this.#thinkWithRetry(messages, runId, { temperature: brain.temperature, profile: brain.profile });
|
| 443 |
const answer = thinkRes.text ?? thinkRes ?? '';
|
| 444 |
const thinkMs = Date.now() - tThink;
|
| 445 |
if (thinkRes.usage) addUsage(thinkRes.usage);
|
|
|
|
| 553 |
messages.push({ role: 'user', content: 'Step limit reached. Reply {"reasoning":"brief summary of what you did","answer":"..."} — or plain text. No more tools.' });
|
| 554 |
try {
|
| 555 |
const tThink = Date.now();
|
| 556 |
+
const thinkRes = await this.#thinkWithRetry(messages, runId, { temperature: brain.temperature, profile: brain.profile });
|
| 557 |
const r2 = parseBrainReply(thinkRes.text ?? '');
|
| 558 |
if (r2 && r2.kind === 'answer') final = r2.answer;
|
| 559 |
else if (r2 && r2.kind === 'text') final = r2.text;
|
|
|
|
| 576 |
|
| 577 |
// Self-verification pass: the brain re-checks its own answer against
|
| 578 |
// the evidence before it reaches the user (fixes premature or sloppy answers).
|
| 579 |
+
if (final && brain.verify) {
|
| 580 |
try {
|
| 581 |
messages.push({ role: 'assistant', content: final });
|
| 582 |
messages.push({ role: 'user', content: 'VERIFY: You are about to send this answer to the user. Check it against the tool results above: is every claim factual, complete and direct? If something is wrong or missing, fix it. Reply {"reasoning":"what you checked","answer":"<corrected or unchanged answer>"}.' });
|
| 583 |
const tThink = Date.now();
|
| 584 |
+
const vRes = await this.#thinkWithRetry(messages, runId, { temperature: brain.temperature, profile: brain.profile });
|
| 585 |
const vr = parseBrainReply(vRes.text ?? '');
|
| 586 |
if (vr && vr.kind === 'answer' && vr.answer.trim()) final = vr.answer;
|
| 587 |
else if (vr && vr.kind === 'text' && (vRes.text ?? '').trim()) final = vRes.text;
|
|
|
|
| 677 |
}
|
| 678 |
}
|
| 679 |
|
| 680 |
+
#toolsPrompt(taskRow, brain = this.#brain) {
|
| 681 |
const rows = tools.list()
|
| 682 |
.map((t) => `- ${t.name}: ${t.description}${t.args ? ` (args: ${JSON.stringify(t.args)})` : ''}`)
|
| 683 |
.join('\n');
|
|
|
|
| 684 |
let p = `You are mona-agent — the AI agent controlling this device (${process.platform}). You reason deeply and act precisely: plan, act, observe, reflect, then answer.
|
| 685 |
|
| 686 |
## Reasoning protocol
|
apps/desktop/src/cloud.js
CHANGED
|
@@ -57,10 +57,10 @@ function b64Body(obj) {
|
|
| 57 |
// onUsage(usage) — called with final token counts (if provided)
|
| 58 |
// Returns { text, usage, model, provider } — usage is null when the
|
| 59 |
// cloud did not report it (older server or plain JSON without usage).
|
| 60 |
-
export async function think({ apiKey, messages, tools, onChunk, onUsage, signal, temperature }) {
|
| 61 |
const res = await apiFetch(P.think, {
|
| 62 |
apiKey,
|
| 63 |
-
body: b64Body({ messages, tools, stream: true, temperature }),
|
| 64 |
signal,
|
| 65 |
});
|
| 66 |
|
|
|
|
| 57 |
// onUsage(usage) — called with final token counts (if provided)
|
| 58 |
// Returns { text, usage, model, provider } — usage is null when the
|
| 59 |
// cloud did not report it (older server or plain JSON without usage).
|
| 60 |
+
export async function think({ apiKey, messages, tools, onChunk, onUsage, signal, temperature, profile }) {
|
| 61 |
const res = await apiFetch(P.think, {
|
| 62 |
apiKey,
|
| 63 |
+
body: b64Body({ messages, tools, stream: true, temperature, profile }),
|
| 64 |
signal,
|
| 65 |
});
|
| 66 |
|