@plune-ai/cli 0.32.2 → 0.34.0

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/dist/index.cjs CHANGED
@@ -457,24 +457,24 @@ function priceFrom(price, usage) {
457
457
  var defaultWarn = (message) => {
458
458
  process.stderr.write(message + "\n");
459
459
  };
460
- function resolveCost(usage, model, reportedCostUsd, overrides, deps = {}) {
460
+ function resolveCostEntry(usage, model, reportedCostUsd, overrides, deps = {}) {
461
461
  const table = deps.table ?? PRICE_TABLE;
462
462
  const warn = deps.warn ?? defaultWarn;
463
463
  const override = overrides?.[model];
464
464
  if (override !== void 0) {
465
- return priceFrom(override, usage);
465
+ return { cost_usd: priceFrom(override, usage), priced: true };
466
466
  }
467
467
  if (reportedCostUsd !== void 0) {
468
- return reportedCostUsd;
468
+ return { cost_usd: reportedCostUsd, priced: true };
469
469
  }
470
470
  const tablePrice = table[model];
471
471
  if (tablePrice !== void 0) {
472
- return priceFrom(tablePrice, usage);
472
+ return { cost_usd: priceFrom(tablePrice, usage), priced: true };
473
473
  }
474
474
  warn(
475
475
  `plune: no price for model "${model}" \u2014 reporting cost_usd=0. Set pricing["${model}"] in plune.yaml to track its cost.`
476
476
  );
477
- return 0;
477
+ return { cost_usd: 0, priced: false };
478
478
  }
479
479
 
480
480
  // src/providers/anthropic.ts
@@ -520,7 +520,7 @@ function makeAnthropicProvider(config, env, pricing) {
520
520
  }
521
521
  },
522
522
  estimateCost(usage, reportedCostUsd) {
523
- return { cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing) };
523
+ return resolveCostEntry(usage, config.model, reportedCostUsd, pricing);
524
524
  }
525
525
  };
526
526
  }
@@ -576,7 +576,7 @@ function makeOpenAiCompatibleProvider(opts) {
576
576
  }
577
577
  },
578
578
  estimateCost(usage, reportedCostUsd) {
579
- return { cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing) };
579
+ return resolveCostEntry(usage, config.model, reportedCostUsd, pricing);
580
580
  }
581
581
  };
582
582
  }
@@ -638,9 +638,7 @@ function makeEstimateProvider(config, pricing) {
638
638
  complete: () => Promise.reject(
639
639
  new Error("The --dry-run provider only estimates cost; it never calls a model.")
640
640
  ),
641
- estimateCost: (usage, reportedCostUsd) => ({
642
- cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing)
643
- })
641
+ estimateCost: (usage, reportedCostUsd) => resolveCostEntry(usage, config.model, reportedCostUsd, pricing)
644
642
  };
645
643
  }
646
644
 
@@ -1426,6 +1424,12 @@ function toRunError(err) {
1426
1424
  function estimateUsage(prompt5, maxTokens) {
1427
1425
  return { input_tokens: Math.ceil(prompt5.length / 4), output_tokens: maxTokens };
1428
1426
  }
1427
+ function pricedField(priced) {
1428
+ return priced === void 0 ? {} : { priced };
1429
+ }
1430
+ function costFields(c) {
1431
+ return { cost_usd: c.cost_usd, ...pricedField(c.priced) };
1432
+ }
1429
1433
  async function runRow(p) {
1430
1434
  const t0 = p.now();
1431
1435
  const prompt5 = resolvePrompt(p.template, p.row.vars);
@@ -1433,7 +1437,8 @@ async function runRow(p) {
1433
1437
  const maxTokens = p.providerConfig.max_tokens ?? DEFAULT_MAX_TOKENS2;
1434
1438
  if (p.dryRun) {
1435
1439
  const est = estimateUsage(prompt5, maxTokens);
1436
- const usage2 = { ...est, cost_usd: p.provider.estimateCost(est).cost_usd };
1440
+ const cost = p.provider.estimateCost(est);
1441
+ const usage2 = { ...est, ...costFields(cost) };
1437
1442
  return { vars: p.row.vars, output: null, cached: false, usage: usage2, assertions: [] };
1438
1443
  }
1439
1444
  const key = cacheKey(
@@ -1471,7 +1476,9 @@ async function runRow(p) {
1471
1476
  usage = {
1472
1477
  input_tokens: hit.usage.input_tokens,
1473
1478
  output_tokens: hit.usage.output_tokens,
1474
- cost_usd: 0
1479
+ cost_usd: 0,
1480
+ // A hit costs nothing, but whether that 0 is a known price depends on the model.
1481
+ ...pricedField(p.provider.estimateCost(hit.usage).priced)
1475
1482
  };
1476
1483
  cacheSaved = {
1477
1484
  input_tokens: hit.usage.input_tokens,
@@ -1488,12 +1495,13 @@ async function runRow(p) {
1488
1495
  prompt_resolved: prompt5
1489
1496
  });
1490
1497
  output = res.output;
1498
+ const cost = p.provider.estimateCost(res.usage, res.cost_usd);
1491
1499
  usage = {
1492
1500
  input_tokens: res.usage.input_tokens,
1493
1501
  output_tokens: res.usage.output_tokens,
1494
1502
  // Prefer the provider-reported actual cost (res.cost_usd) when present; resolveCost still
1495
1503
  // lets a config pricing override win, else falls back to the table estimate (ADR-PRC01).
1496
- cost_usd: p.provider.estimateCost(res.usage, res.cost_usd).cost_usd
1504
+ ...costFields(cost)
1497
1505
  };
1498
1506
  if (!p.noCache && isCacheable(res) && answerParses(res.output, p.assertions)) {
1499
1507
  p.cache.set(key, res);
@@ -1510,8 +1518,11 @@ async function runRow(p) {
1510
1518
  }
1511
1519
  }
1512
1520
  let judgeCost = 0;
1521
+ let judgeUnpriced = false;
1513
1522
  const judge = buildJudge(p.provider, p.providerConfig, (u) => {
1514
- judgeCost += p.provider.estimateCost(u).cost_usd;
1523
+ const c = p.provider.estimateCost(u);
1524
+ judgeCost += c.cost_usd;
1525
+ if (c.priced === false) judgeUnpriced = true;
1515
1526
  });
1516
1527
  const records = [];
1517
1528
  let assertionError;
@@ -1536,7 +1547,12 @@ async function runRow(p) {
1536
1547
  break;
1537
1548
  }
1538
1549
  }
1539
- const finalUsage = { ...usage, cost_usd: usage.cost_usd + judgeCost };
1550
+ const finalUsage = {
1551
+ ...usage,
1552
+ cost_usd: usage.cost_usd + judgeCost,
1553
+ // A row is priced only when every call in it was; the field stays absent if the provider never said.
1554
+ ...judgeUnpriced ? { priced: false } : {}
1555
+ };
1540
1556
  return {
1541
1557
  vars: p.row.vars,
1542
1558
  output,
@@ -1758,7 +1774,9 @@ var assertionResultRecordSchema2 = import_zod3.z.object({
1758
1774
  var usageSchema = import_zod3.z.object({
1759
1775
  input_tokens: import_zod3.z.number(),
1760
1776
  output_tokens: import_zod3.z.number(),
1761
- cost_usd: import_zod3.z.number()
1777
+ cost_usd: import_zod3.z.number(),
1778
+ /** cli#151. Optional and additive — a RunResult written before it validates unchanged. */
1779
+ priced: import_zod3.z.boolean().optional()
1762
1780
  });
1763
1781
  var runErrorSchema = import_zod3.z.object({
1764
1782
  code: import_zod3.z.string(),
package/dist/index.d.cts CHANGED
@@ -44,6 +44,12 @@ interface Usage {
44
44
  input_tokens: number;
45
45
  output_tokens: number;
46
46
  cost_usd: number;
47
+ /**
48
+ * plune-ai/cli#151. `true` when the price of the model is known, a zero price included (a `:free`
49
+ * model, `pricing: 0`); `false` when nobody has a price and `cost_usd` is a placeholder 0. Optional
50
+ * and additive: a report without it reads as before, and `schemaVersion` stays 1.
51
+ */
52
+ priced?: boolean;
47
53
  }
48
54
  interface RunError {
49
55
  code: string;
@@ -263,6 +269,8 @@ interface CompletionResponse {
263
269
  }
264
270
  interface CostEstimate {
265
271
  cost_usd: number;
272
+ /** Whether `cost_usd` is a known price (zero included) rather than the 0 of an unpriced model. Optional and additive. */
273
+ priced?: boolean;
266
274
  }
267
275
  interface Provider<_TConfig extends ProviderConfig = ProviderConfig> {
268
276
  complete(req: CompletionRequest): Promise<CompletionResponse>;
@@ -510,6 +518,7 @@ declare const runResultSchema: z.ZodObject<{
510
518
  input_tokens: z.ZodNumber;
511
519
  output_tokens: z.ZodNumber;
512
520
  cost_usd: z.ZodNumber;
521
+ priced: z.ZodOptional<z.ZodBoolean>;
513
522
  }, z.core.$strip>>;
514
523
  latency_ms: z.ZodOptional<z.ZodNumber>;
515
524
  error: z.ZodOptional<z.ZodObject<{
package/dist/index.d.ts CHANGED
@@ -44,6 +44,12 @@ interface Usage {
44
44
  input_tokens: number;
45
45
  output_tokens: number;
46
46
  cost_usd: number;
47
+ /**
48
+ * plune-ai/cli#151. `true` when the price of the model is known, a zero price included (a `:free`
49
+ * model, `pricing: 0`); `false` when nobody has a price and `cost_usd` is a placeholder 0. Optional
50
+ * and additive: a report without it reads as before, and `schemaVersion` stays 1.
51
+ */
52
+ priced?: boolean;
47
53
  }
48
54
  interface RunError {
49
55
  code: string;
@@ -263,6 +269,8 @@ interface CompletionResponse {
263
269
  }
264
270
  interface CostEstimate {
265
271
  cost_usd: number;
272
+ /** Whether `cost_usd` is a known price (zero included) rather than the 0 of an unpriced model. Optional and additive. */
273
+ priced?: boolean;
266
274
  }
267
275
  interface Provider<_TConfig extends ProviderConfig = ProviderConfig> {
268
276
  complete(req: CompletionRequest): Promise<CompletionResponse>;
@@ -510,6 +518,7 @@ declare const runResultSchema: z.ZodObject<{
510
518
  input_tokens: z.ZodNumber;
511
519
  output_tokens: z.ZodNumber;
512
520
  cost_usd: z.ZodNumber;
521
+ priced: z.ZodOptional<z.ZodBoolean>;
513
522
  }, z.core.$strip>>;
514
523
  latency_ms: z.ZodOptional<z.ZodNumber>;
515
524
  error: z.ZodOptional<z.ZodObject<{
package/dist/index.js CHANGED
@@ -397,24 +397,24 @@ function priceFrom(price, usage) {
397
397
  var defaultWarn = (message) => {
398
398
  process.stderr.write(message + "\n");
399
399
  };
400
- function resolveCost(usage, model, reportedCostUsd, overrides, deps = {}) {
400
+ function resolveCostEntry(usage, model, reportedCostUsd, overrides, deps = {}) {
401
401
  const table = deps.table ?? PRICE_TABLE;
402
402
  const warn = deps.warn ?? defaultWarn;
403
403
  const override = overrides?.[model];
404
404
  if (override !== void 0) {
405
- return priceFrom(override, usage);
405
+ return { cost_usd: priceFrom(override, usage), priced: true };
406
406
  }
407
407
  if (reportedCostUsd !== void 0) {
408
- return reportedCostUsd;
408
+ return { cost_usd: reportedCostUsd, priced: true };
409
409
  }
410
410
  const tablePrice = table[model];
411
411
  if (tablePrice !== void 0) {
412
- return priceFrom(tablePrice, usage);
412
+ return { cost_usd: priceFrom(tablePrice, usage), priced: true };
413
413
  }
414
414
  warn(
415
415
  `plune: no price for model "${model}" \u2014 reporting cost_usd=0. Set pricing["${model}"] in plune.yaml to track its cost.`
416
416
  );
417
- return 0;
417
+ return { cost_usd: 0, priced: false };
418
418
  }
419
419
 
420
420
  // src/providers/anthropic.ts
@@ -460,7 +460,7 @@ function makeAnthropicProvider(config, env, pricing) {
460
460
  }
461
461
  },
462
462
  estimateCost(usage, reportedCostUsd) {
463
- return { cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing) };
463
+ return resolveCostEntry(usage, config.model, reportedCostUsd, pricing);
464
464
  }
465
465
  };
466
466
  }
@@ -516,7 +516,7 @@ function makeOpenAiCompatibleProvider(opts) {
516
516
  }
517
517
  },
518
518
  estimateCost(usage, reportedCostUsd) {
519
- return { cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing) };
519
+ return resolveCostEntry(usage, config.model, reportedCostUsd, pricing);
520
520
  }
521
521
  };
522
522
  }
@@ -578,9 +578,7 @@ function makeEstimateProvider(config, pricing) {
578
578
  complete: () => Promise.reject(
579
579
  new Error("The --dry-run provider only estimates cost; it never calls a model.")
580
580
  ),
581
- estimateCost: (usage, reportedCostUsd) => ({
582
- cost_usd: resolveCost(usage, config.model, reportedCostUsd, pricing)
583
- })
581
+ estimateCost: (usage, reportedCostUsd) => resolveCostEntry(usage, config.model, reportedCostUsd, pricing)
584
582
  };
585
583
  }
586
584
 
@@ -1366,6 +1364,12 @@ function toRunError(err) {
1366
1364
  function estimateUsage(prompt5, maxTokens) {
1367
1365
  return { input_tokens: Math.ceil(prompt5.length / 4), output_tokens: maxTokens };
1368
1366
  }
1367
+ function pricedField(priced) {
1368
+ return priced === void 0 ? {} : { priced };
1369
+ }
1370
+ function costFields(c) {
1371
+ return { cost_usd: c.cost_usd, ...pricedField(c.priced) };
1372
+ }
1369
1373
  async function runRow(p) {
1370
1374
  const t0 = p.now();
1371
1375
  const prompt5 = resolvePrompt(p.template, p.row.vars);
@@ -1373,7 +1377,8 @@ async function runRow(p) {
1373
1377
  const maxTokens = p.providerConfig.max_tokens ?? DEFAULT_MAX_TOKENS2;
1374
1378
  if (p.dryRun) {
1375
1379
  const est = estimateUsage(prompt5, maxTokens);
1376
- const usage2 = { ...est, cost_usd: p.provider.estimateCost(est).cost_usd };
1380
+ const cost = p.provider.estimateCost(est);
1381
+ const usage2 = { ...est, ...costFields(cost) };
1377
1382
  return { vars: p.row.vars, output: null, cached: false, usage: usage2, assertions: [] };
1378
1383
  }
1379
1384
  const key = cacheKey(
@@ -1411,7 +1416,9 @@ async function runRow(p) {
1411
1416
  usage = {
1412
1417
  input_tokens: hit.usage.input_tokens,
1413
1418
  output_tokens: hit.usage.output_tokens,
1414
- cost_usd: 0
1419
+ cost_usd: 0,
1420
+ // A hit costs nothing, but whether that 0 is a known price depends on the model.
1421
+ ...pricedField(p.provider.estimateCost(hit.usage).priced)
1415
1422
  };
1416
1423
  cacheSaved = {
1417
1424
  input_tokens: hit.usage.input_tokens,
@@ -1428,12 +1435,13 @@ async function runRow(p) {
1428
1435
  prompt_resolved: prompt5
1429
1436
  });
1430
1437
  output = res.output;
1438
+ const cost = p.provider.estimateCost(res.usage, res.cost_usd);
1431
1439
  usage = {
1432
1440
  input_tokens: res.usage.input_tokens,
1433
1441
  output_tokens: res.usage.output_tokens,
1434
1442
  // Prefer the provider-reported actual cost (res.cost_usd) when present; resolveCost still
1435
1443
  // lets a config pricing override win, else falls back to the table estimate (ADR-PRC01).
1436
- cost_usd: p.provider.estimateCost(res.usage, res.cost_usd).cost_usd
1444
+ ...costFields(cost)
1437
1445
  };
1438
1446
  if (!p.noCache && isCacheable(res) && answerParses(res.output, p.assertions)) {
1439
1447
  p.cache.set(key, res);
@@ -1450,8 +1458,11 @@ async function runRow(p) {
1450
1458
  }
1451
1459
  }
1452
1460
  let judgeCost = 0;
1461
+ let judgeUnpriced = false;
1453
1462
  const judge = buildJudge(p.provider, p.providerConfig, (u) => {
1454
- judgeCost += p.provider.estimateCost(u).cost_usd;
1463
+ const c = p.provider.estimateCost(u);
1464
+ judgeCost += c.cost_usd;
1465
+ if (c.priced === false) judgeUnpriced = true;
1455
1466
  });
1456
1467
  const records = [];
1457
1468
  let assertionError;
@@ -1476,7 +1487,12 @@ async function runRow(p) {
1476
1487
  break;
1477
1488
  }
1478
1489
  }
1479
- const finalUsage = { ...usage, cost_usd: usage.cost_usd + judgeCost };
1490
+ const finalUsage = {
1491
+ ...usage,
1492
+ cost_usd: usage.cost_usd + judgeCost,
1493
+ // A row is priced only when every call in it was; the field stays absent if the provider never said.
1494
+ ...judgeUnpriced ? { priced: false } : {}
1495
+ };
1480
1496
  return {
1481
1497
  vars: p.row.vars,
1482
1498
  output,
@@ -1698,7 +1714,9 @@ var assertionResultRecordSchema2 = z3.object({
1698
1714
  var usageSchema = z3.object({
1699
1715
  input_tokens: z3.number(),
1700
1716
  output_tokens: z3.number(),
1701
- cost_usd: z3.number()
1717
+ cost_usd: z3.number(),
1718
+ /** cli#151. Optional and additive — a RunResult written before it validates unchanged. */
1719
+ priced: z3.boolean().optional()
1702
1720
  });
1703
1721
  var runErrorSchema = z3.object({
1704
1722
  code: z3.string(),