@blockrun/franklin 3.15.94 → 3.15.96

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.
@@ -46,6 +46,19 @@ export interface StreamChunk {
46
46
  export interface CompletionUsage {
47
47
  inputTokens: number;
48
48
  outputTokens: number;
49
+ /**
50
+ * Anthropic prompt-cache fields. `input_tokens` only counts the base
51
+ * (uncached) portion; the cache-creation and cache-read counts are
52
+ * separate and billed at different rates (1.25× / 0.1× of base input,
53
+ * respectively). Pre-fix, Franklin only read `input_tokens` and
54
+ * silently undercounted every vision / cache-using call's total
55
+ * token spend — verified 2026-05-11 from an Opus 4.7 turn billed
56
+ * $0.567 with audit logging `inputTokens: 3653` (implies ~113K real
57
+ * billed input tokens). Surface all three so audits, stats, and any
58
+ * future estimation paths see the full picture.
59
+ */
60
+ cacheCreationInputTokens?: number;
61
+ cacheReadInputTokens?: number;
49
62
  }
50
63
  export interface LLMClientOptions {
51
64
  apiUrl: string;
package/dist/agent/llm.js CHANGED
@@ -864,6 +864,15 @@ export class ModelClient {
864
864
  const msgUsage = chunk.payload['usage'];
865
865
  if (msgUsage) {
866
866
  usage.outputTokens = msgUsage['output_tokens'] ?? usage.outputTokens;
867
+ // Cache and tool-call breakdowns can arrive in message_delta
868
+ // too; merge whatever's present without clobbering values set
869
+ // by message_start.
870
+ if (msgUsage['cache_creation_input_tokens'] !== undefined) {
871
+ usage.cacheCreationInputTokens = msgUsage['cache_creation_input_tokens'];
872
+ }
873
+ if (msgUsage['cache_read_input_tokens'] !== undefined) {
874
+ usage.cacheReadInputTokens = msgUsage['cache_read_input_tokens'];
875
+ }
867
876
  }
868
877
  const delta = chunk.payload['delta'];
869
878
  if (delta?.['stop_reason']) {
@@ -877,6 +886,18 @@ export class ModelClient {
877
886
  if (msgUsage) {
878
887
  usage.inputTokens = msgUsage['input_tokens'] ?? 0;
879
888
  usage.outputTokens = msgUsage['output_tokens'] ?? 0;
889
+ // Vision and prompt-cache calls return up to two extra
890
+ // billed-tokens counts that input_tokens does NOT include:
891
+ // cache_creation_input_tokens (1.25× base price) and
892
+ // cache_read_input_tokens (0.1× base price). Without these,
893
+ // any audit/stats over a vision-heavy session looks wildly
894
+ // inconsistent with the wallet charge.
895
+ if (msgUsage['cache_creation_input_tokens'] !== undefined) {
896
+ usage.cacheCreationInputTokens = msgUsage['cache_creation_input_tokens'];
897
+ }
898
+ if (msgUsage['cache_read_input_tokens'] !== undefined) {
899
+ usage.cacheReadInputTokens = msgUsage['cache_read_input_tokens'];
900
+ }
880
901
  }
881
902
  break;
882
903
  }
@@ -1656,6 +1656,8 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
1656
1656
  model: resolvedModel,
1657
1657
  inputTokens,
1658
1658
  outputTokens: usage.outputTokens,
1659
+ cacheCreationInputTokens: usage.cacheCreationInputTokens,
1660
+ cacheReadInputTokens: usage.cacheReadInputTokens,
1659
1661
  costUsd: callCost,
1660
1662
  // Any failed model this turn means the model that finally
1661
1663
  // succeeded was a fallback. Without this, audit log read 0%
@@ -108,13 +108,27 @@ async function runChecks() {
108
108
  status: 'ok',
109
109
  detail: `${walletAddress.slice(0, 10)}…${walletAddress.slice(-6)}`,
110
110
  });
111
+ // Tiered balance status. Binary `> 0` was misleading — verified
112
+ // 2026-05-11 from a real run: doctor printed `✓ USDC balance
113
+ // $0.37` (green) on a wallet that couldn't fund a single Opus
114
+ // call ($0.50+ each). Threshold of $1.00 covers ~10 cheap-model
115
+ // calls or ~2 mid-tier calls — anything below that is
116
+ // operationally empty for paid workflows. Free models still work.
117
+ const LOW_BALANCE_THRESHOLD = 1.00;
118
+ const balanceStatus = walletBalance >= LOW_BALANCE_THRESHOLD ? 'ok' : 'warn';
119
+ const balanceDetail = walletBalance === 0
120
+ ? '$0.00 — free-tier models only (no paid calls possible)'
121
+ : walletBalance < LOW_BALANCE_THRESHOLD
122
+ ? `$${walletBalance.toFixed(2)} — low; paid calls likely to fail mid-stream`
123
+ : `$${walletBalance.toFixed(2)}`;
124
+ const balanceRemedy = walletBalance < LOW_BALANCE_THRESHOLD
125
+ ? `Send USDC on ${chain} to ${walletAddress} (or open http://localhost:3100/#wallet)`
126
+ : undefined;
111
127
  out.push({
112
128
  name: 'USDC balance',
113
- status: walletBalance > 0 ? 'ok' : 'warn',
114
- detail: `$${walletBalance.toFixed(2)}${walletBalance === 0 ? ' — free-tier models only' : ''}`,
115
- remedy: walletBalance === 0
116
- ? `Send USDC on ${chain} to ${walletAddress} to unlock paid models`
117
- : undefined,
129
+ status: balanceStatus,
130
+ detail: balanceDetail,
131
+ remedy: balanceRemedy,
118
132
  });
119
133
  }
120
134
  catch (err) {
@@ -14,6 +14,18 @@ export interface AuditEntry {
14
14
  model: string;
15
15
  inputTokens: number;
16
16
  outputTokens: number;
17
+ /**
18
+ * Anthropic prompt-cache fields, captured when the model reports them
19
+ * in `usage.cache_creation_input_tokens` / `usage.cache_read_input_tokens`.
20
+ * `inputTokens` above is the *uncached* portion; the cache fields are
21
+ * additional billed input the gateway charges for separately. Without
22
+ * these, vision and cache-heavy sessions show a wildly inconsistent
23
+ * cost-per-token ratio in audit dashboards — verified 2026-05-11 from
24
+ * an Opus 4.7 call with inputTokens=3653 but costUsd=$0.567 (implies
25
+ * ~113K real billed tokens once cache_creation is counted).
26
+ */
27
+ cacheCreationInputTokens?: number;
28
+ cacheReadInputTokens?: number;
17
29
  costUsd: number;
18
30
  latencyMs?: number;
19
31
  fallback?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.94",
3
+ "version": "3.15.96",
4
4
  "description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
5
5
  "type": "module",
6
6
  "exports": {