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 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 < this.#brain.maxSteps; 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 && this.#brain.verify) {
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