terminal-smart-cli 0.94.0 → 0.94.1

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/lib/eval.js +42 -10
  2. package/package.json +2 -2
package/lib/eval.js CHANGED
@@ -20,6 +20,7 @@ const fs = require('fs');
20
20
  const path = require('path');
21
21
  const { api } = require('./api');
22
22
  const agent = require('./agent');
23
+ const intelligence = require('./intelligence-core');
23
24
 
24
25
  // modelo default do JUIZ: barato e suficiente pra avaliar critério objetivo.
25
26
  const DEFAULT_JUDGE = 'deepseek-v4-flash';
@@ -163,6 +164,7 @@ async function runSuite(suite, opts = {}) {
163
164
  let sandbox = null;
164
165
  let evidence = { text: '', error: null, diff: { criados: [], alterados: [], removidos: [] } };
165
166
  let runCredits = 0, runSteps = 0, verdict = null, judgeTin = 0, judgeTout = 0;
167
+ let actualModel = null, substituted = false;
166
168
  const t0 = Date.now();
167
169
  try {
168
170
  // sandbox DENTRO do try: se mkdtempSync falhar (EMFILE/disco cheio/permissão) o caso
@@ -192,17 +194,33 @@ async function runSuite(suite, opts = {}) {
192
194
  evidence.error = String((e && e.message) || e).slice(0, 300);
193
195
  out = null;
194
196
  }
195
- if (out) { evidence.text = out.text || ''; runCredits = out.credits || 0; runSteps = out.steps || 0; }
197
+ if (out) {
198
+ evidence.text = out.text || '';
199
+ runCredits = out.credits || 0;
200
+ runSteps = out.steps || 0;
201
+ actualModel = out.model || null;
202
+ substituted = !_sameRequestedModel(model, actualModel);
203
+ if (substituted) {
204
+ evidence.error = `model_substituted: requested=${model}; actual=${actualModel || 'unknown'}`;
205
+ verdict = {
206
+ pass: false,
207
+ score: 0,
208
+ reason: `modelo solicitado "${model}" foi substituído por "${actualModel || 'desconhecido'}"; resultado não é comparável`,
209
+ };
210
+ }
211
+ }
196
212
  const after = _snap(sandbox);
197
213
  evidence.diff = _diff(before, after);
198
214
  // julga (mesmo com erro fatal — o juiz vê o erro e reprova com motivo)
199
- onCase({ i, total, id: c.id, phase: 'judge' });
200
- try {
201
- verdict = await judge({ key: k.key, baseUrl: k.baseUrl, model: judgeModel, caseObj: c, evidence });
202
- judgeTin = verdict._tin || 0; judgeTout = verdict._tout || 0;
203
- } catch (e) {
204
- if (e && (e.code === 'no_credits' || e.status === 402)) throw e;
205
- verdict = { pass: false, score: 0, reason: 'juiz falhou: ' + String((e && e.message) || e).slice(0, 120) };
215
+ if (!substituted) {
216
+ onCase({ i, total, id: c.id, phase: 'judge' });
217
+ try {
218
+ verdict = await judge({ key: k.key, baseUrl: k.baseUrl, model: judgeModel, caseObj: c, evidence });
219
+ judgeTin = verdict._tin || 0; judgeTout = verdict._tout || 0;
220
+ } catch (e) {
221
+ if (e && (e.code === 'no_credits' || e.status === 402)) throw e;
222
+ verdict = { pass: false, score: 0, reason: 'juiz falhou: ' + String((e && e.message) || e).slice(0, 120) };
223
+ }
206
224
  }
207
225
  } catch (e) {
208
226
  // teto de IA → aborta a suíte inteira (o finally limpa o sandbox antes de propagar).
@@ -219,6 +237,7 @@ async function runSuite(suite, opts = {}) {
219
237
  results.push({
220
238
  id: c.id, pass: verdict.pass, score: verdict.score, reason: verdict.reason,
221
239
  error: evidence.error, steps: runSteps, credits: runCredits,
240
+ requestedModel: model, actualModel, substituted,
222
241
  changed: evidence.diff.criados.length + evidence.diff.alterados.length,
223
242
  judgeTokens: judgeTin + judgeTout, ms: Date.now() - t0, judgeRaw: verdict._raw || null,
224
243
  });
@@ -235,6 +254,8 @@ async function runSuite(suite, opts = {}) {
235
254
  // exibidos à parte pra o custo do relatório ser honesto).
236
255
  judgeTokens: results.reduce((a, r) => a + (r.judgeTokens || 0), 0),
237
256
  ms: results.reduce((a, r) => a + (r.ms || 0), 0),
257
+ actualModels: [...new Set(results.map(r => r.actualModel).filter(Boolean))],
258
+ substitutions: results.filter(r => r.substituted).length,
238
259
  };
239
260
  return { summary, results };
240
261
  }
@@ -242,6 +263,12 @@ async function runSuite(suite, opts = {}) {
242
263
  // ── MODEL LAB: roda a MESMA suíte em VÁRIOS executores e compara (bake-off automático) ──
243
264
  // Automatiza o "veredito modelo X" que antes era manual. O JUIZ é fixo (mesmo pra todos =
244
265
  // comparação justa); só o EXECUTOR muda. Ranqueia por qualidade VERIFICADA e depois por custo.
266
+ function _sameRequestedModel(requested, actual) {
267
+ if (!requested) return true;
268
+ if (!actual) return false;
269
+ return intelligence.normalizeModelId(requested) === intelligence.normalizeModelId(actual);
270
+ }
271
+
245
272
  function _rankModels(rows) {
246
273
  // ordena: mais casos aprovados (passRate) → maior score médio → MENOR custo → mais rápido.
247
274
  const sorted = [...rows].sort((a, b) =>
@@ -264,7 +291,12 @@ async function runMatrix(suite, opts = {}) {
264
291
  if (e && (e.code === 'no_credits' || e.status === 402)) throw e; // teto → aborta o bake-off
265
292
  sum = { name: suite.name, total: suite.cases.length, passed: 0, failed: suite.cases.length, passRate: 0, avgScore: 0, credits: 0, judgeTokens: 0, ms: 0, error: String((e && e.message) || e).slice(0, 160) };
266
293
  }
267
- const row = { model, passRate: sum.passRate, avgScore: sum.avgScore, passed: sum.passed, total: sum.total, credits: sum.credits, judgeTokens: sum.judgeTokens, ms: sum.ms, error: sum.error || null };
294
+ const row = {
295
+ model, passRate: sum.passRate, avgScore: sum.avgScore, passed: sum.passed, total: sum.total,
296
+ credits: sum.credits, judgeTokens: sum.judgeTokens, ms: sum.ms,
297
+ actualModels: sum.actualModels || [], substitutions: sum.substitutions || 0,
298
+ error: sum.error || null,
299
+ };
268
300
  rows.push(row);
269
301
  onModel({ model, phase: 'done', row });
270
302
  }
@@ -273,4 +305,4 @@ async function runMatrix(suite, opts = {}) {
273
305
  }
274
306
 
275
307
  module.exports = { loadSuite, normCases, runSuite, runMatrix, judge, smokeSuite, EXAMPLE,
276
- _test: { _diff, _snap, _extractJson, normCases, _rankModels } };
308
+ _test: { _diff, _snap, _extractJson, normCases, _rankModels, _sameRequestedModel } };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.94.0",
3
+ "version": "0.94.1",
4
4
  "description": "Terminal Smart no seu terminal — pergunte, analise logs por pipe e orquestre agentes de IA. Comando: ts",
5
5
  "bin": {
6
6
  "ts": "bin/ts.js"
7
7
  },
8
8
  "scripts": {
9
- "test": "node test/core.test.js && node test/intelligence-core.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js"
9
+ "test": "node test/core.test.js && node test/intelligence-core.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js"
10
10
  },
11
11
  "files": [
12
12
  "bin",