mohdel 0.94.0 → 0.96.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.
@@ -14,6 +14,19 @@ import { getSpec, setCatalog } from './_catalog.js'
14
14
  /**
15
15
  * Pure cost computation from spec + usage.
16
16
  *
17
+ * Each price field (`inputPrice` / `outputPrice` / `thinkingPrice`) is
18
+ * one of:
19
+ *
20
+ * - a `number` — flat per-million rate; or
21
+ * - an object `{">N": number, ..., "default": number}` — tiered.
22
+ * The active rate is the one under the highest `>N` key that the
23
+ * call's `inputTokens` exceeds; falls back to `"default"` when
24
+ * nothing matches. Keys that aren't `">N"` or `"default"` are
25
+ * ignored. `>` is strict — at exactly N, the default is used.
26
+ *
27
+ * `thinkingPrice` is optional and falls back to the resolved
28
+ * `outputPrice` when absent.
29
+ *
17
30
  * @param {any} spec Catalog entry (with `inputPrice`/`outputPrice`/`thinkingPrice`),
18
31
  * or `undefined`.
19
32
  * @param {{inputTokens?: number, outputTokens?: number, thinkingTokens?: number}} usage
@@ -21,17 +34,50 @@ import { getSpec, setCatalog } from './_catalog.js'
21
34
  */
22
35
  export function computeCost (spec, usage) {
23
36
  if (!spec) return 0
24
- const ip = spec.inputPrice
25
- const op = spec.outputPrice
26
- if (typeof ip !== 'number' || typeof op !== 'number') return 0
27
37
  const i = usage.inputTokens ?? 0
28
38
  const o = usage.outputTokens ?? 0
29
39
  const t = usage.thinkingTokens ?? 0
30
- const tp = typeof spec.thinkingPrice === 'number' ? spec.thinkingPrice : op
31
- const total = (i * ip + o * op + t * tp) / 1_000_000
40
+ const ip = resolveTier(spec.inputPrice, i)
41
+ const op = resolveTier(spec.outputPrice, i)
42
+ if (typeof ip !== 'number' || typeof op !== 'number') return 0
43
+ const tp = resolveTier(spec.thinkingPrice, i)
44
+ const tpFinal = typeof tp === 'number' ? tp : op
45
+ const total = (i * ip + o * op + t * tpFinal) / 1_000_000
32
46
  return round(total)
33
47
  }
34
48
 
49
+ /**
50
+ * Resolve a price field against a token count. Scalars pass through;
51
+ * tiered maps return the rate of the highest `>N` key that
52
+ * `tokens` exceeds, falling back to `default`. Returns `null` when
53
+ * the field is absent or malformed — callers decide whether to treat
54
+ * that as "no price" (cost=0) or fall back (thinkingPrice→outputPrice).
55
+ *
56
+ * @param {unknown} price
57
+ * @param {number} tokens
58
+ * @returns {number | null}
59
+ */
60
+ function resolveTier (price, tokens) {
61
+ if (typeof price === 'number') return price
62
+ if (!price || typeof price !== 'object') return null
63
+ let best = null
64
+ let bestThreshold = -1
65
+ for (const key of Object.keys(price)) {
66
+ if (key === 'default') continue
67
+ const m = /^>(\d+)$/.exec(key)
68
+ if (!m) continue
69
+ const threshold = Number(m[1])
70
+ if (tokens > threshold && threshold > bestThreshold) {
71
+ bestThreshold = threshold
72
+ const v = /** @type {Record<string, unknown>} */ (price)[key]
73
+ if (typeof v === 'number') best = v
74
+ }
75
+ }
76
+ if (best != null) return best
77
+ const d = /** @type {Record<string, unknown>} */ (price).default
78
+ return typeof d === 'number' ? d : null
79
+ }
80
+
35
81
  /**
36
82
  * @param {string} model Fully-qualified `<provider>/<model>`.
37
83
  * @param {{inputTokens?: number, outputTokens?: number, thinkingTokens?: number}} usage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mohdel",
3
- "version": "0.94.0",
3
+ "version": "0.96.0",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Christophe Le Bars",
@@ -87,7 +87,7 @@
87
87
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.215.0",
88
88
  "@opentelemetry/sdk-node": "^0.215.0",
89
89
  "chalk": "^5.4.0",
90
- "mohdel-thin-gate-linux-x64-gnu": "0.94.0"
90
+ "mohdel-thin-gate-linux-x64-gnu": "0.96.0"
91
91
  },
92
92
  "dependencies": {
93
93
  "@anthropic-ai/sdk": "^0.91.0",
@@ -103,7 +103,7 @@
103
103
  },
104
104
  "devDependencies": {
105
105
  "lint-staged": "^16.4.0",
106
- "release-it": "^20.0.0",
106
+ "release-it": "^20.0.1",
107
107
  "standard": "^17.1.2",
108
108
  "vitest": "^4.1.5"
109
109
  }