trickle-cli 0.1.205 → 0.1.207
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/commands/cost-report.js +26 -5
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +102 -0
- package/dist/index.js +9 -0
- package/package.json +1 -1
- package/src/commands/cost-report.ts +29 -5
- package/src/commands/memory.ts +63 -0
- package/src/index.ts +10 -0
|
@@ -301,12 +301,33 @@ function costReportCommand(opts) {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
-
|
|
304
|
+
// Provider-reported cache tokens (Anthropic cache_read/cache_creation)
|
|
305
|
+
const cacheReadTotal = calls.reduce((s, c) => s + (c.cacheReadTokens || 0), 0);
|
|
306
|
+
const cacheWriteTotal = calls.reduce((s, c) => s + (c.cacheWriteTokens || 0), 0);
|
|
307
|
+
const callsWithCache = calls.filter((c) => c.cacheReadTokens > 0 || c.cacheWriteTokens > 0);
|
|
308
|
+
if (callsWithCache.length > 0 || cacheDetected) {
|
|
305
309
|
console.log(chalk_1.default.gray('\n ' + '─'.repeat(60)));
|
|
306
|
-
console.log(chalk_1.default.bold(' Cache Analysis')
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
+
console.log(chalk_1.default.bold(' Cache Analysis'));
|
|
311
|
+
if (callsWithCache.length > 0) {
|
|
312
|
+
const cacheHitCalls = calls.filter((c) => c.cacheReadTokens > 0);
|
|
313
|
+
const hitRate = calls.length > 0 ? Math.round((cacheHitCalls.length / calls.length) * 100) : 0;
|
|
314
|
+
// Estimate savings: cached tokens cost ~90% less
|
|
315
|
+
const savedTokens = cacheReadTotal;
|
|
316
|
+
const avgInputPrice = totalCost > 0 && totalTokens > 0 ? (totalCost / totalTokens) : 0.000003;
|
|
317
|
+
const estimatedSavings = savedTokens * avgInputPrice * 0.9;
|
|
318
|
+
console.log(chalk_1.default.gray(' Provider-reported cache tokens:'));
|
|
319
|
+
console.log(` Hit rate: ${chalk_1.default.green(hitRate + '%')} (${cacheHitCalls.length}/${calls.length} calls used cache)`);
|
|
320
|
+
console.log(` Cache read: ${formatTokens(cacheReadTotal)} tokens | Cache write: ${formatTokens(cacheWriteTotal)} tokens`);
|
|
321
|
+
if (estimatedSavings > 0) {
|
|
322
|
+
console.log(` Estimated savings: ${chalk_1.default.green('~$' + estimatedSavings.toFixed(4))} from cached tokens`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (cacheDetected) {
|
|
326
|
+
console.log(chalk_1.default.gray(' Latency-based detection:'));
|
|
327
|
+
for (const ca of cacheAnalysis) {
|
|
328
|
+
const speedup = (ca.slowAvg / Math.max(1, ca.fastAvg)).toFixed(0);
|
|
329
|
+
console.log(` ${chalk_1.default.cyan(ca.model.padEnd(25))} hit rate: ${chalk_1.default.green(ca.hitRate + '%')} (${ca.fastCalls} fast, ${ca.slowCalls} slow) ${speedup}x speedup`);
|
|
330
|
+
}
|
|
310
331
|
}
|
|
311
332
|
}
|
|
312
333
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* trickle memory — Show captured agent memory operations (Mem0).
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.memoryCommand = memoryCommand;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
+
function memoryCommand(opts) {
|
|
47
|
+
const dir = process.env.TRICKLE_LOCAL_DIR || path.join(process.cwd(), '.trickle');
|
|
48
|
+
const memFile = path.join(dir, 'memory.jsonl');
|
|
49
|
+
if (!fs.existsSync(memFile)) {
|
|
50
|
+
console.log(chalk_1.default.yellow(' No memory data. Run an app that uses Mem0 with trickle.'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const events = fs.readFileSync(memFile, 'utf-8').split('\n').filter(Boolean)
|
|
54
|
+
.map(l => { try {
|
|
55
|
+
return JSON.parse(l);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
} })
|
|
60
|
+
.filter((e) => e && e.kind === 'memory_op');
|
|
61
|
+
if (events.length === 0) {
|
|
62
|
+
console.log(chalk_1.default.yellow(' No memory operations captured.'));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (opts.json) {
|
|
66
|
+
console.log(JSON.stringify(events, null, 2));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(chalk_1.default.bold(' trickle memory'));
|
|
71
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(60)));
|
|
72
|
+
const adds = events.filter((e) => e.operation === 'add');
|
|
73
|
+
const searches = events.filter((e) => e.operation === 'search');
|
|
74
|
+
const gets = events.filter((e) => e.operation === 'get' || e.operation === 'get_all');
|
|
75
|
+
const updates = events.filter((e) => e.operation === 'update');
|
|
76
|
+
const deletes = events.filter((e) => e.operation === 'delete');
|
|
77
|
+
const errors = events.filter((e) => e.error);
|
|
78
|
+
console.log(` ${chalk_1.default.cyan(String(events.length))} operations: ${adds.length} add, ${searches.length} search, ${gets.length} get, ${updates.length} update, ${deletes.length} delete${errors.length > 0 ? chalk_1.default.red(`, ${errors.length} errors`) : ''}`);
|
|
79
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(60)));
|
|
80
|
+
for (const e of events.slice(-15)) {
|
|
81
|
+
const op = e.operation;
|
|
82
|
+
const dur = e.durationMs ? chalk_1.default.gray(`${e.durationMs}ms`) : '';
|
|
83
|
+
const icon = op === 'add' ? chalk_1.default.green('+') : op === 'search' ? chalk_1.default.blue('?') : op === 'delete' ? chalk_1.default.red('-') : op === 'update' ? chalk_1.default.yellow('~') : chalk_1.default.gray('→');
|
|
84
|
+
let detail = '';
|
|
85
|
+
if (e.input)
|
|
86
|
+
detail = e.input.substring(0, 60);
|
|
87
|
+
if (e.query)
|
|
88
|
+
detail = `"${e.query.substring(0, 50)}"`;
|
|
89
|
+
if (e.memoryId)
|
|
90
|
+
detail = `id:${e.memoryId}`;
|
|
91
|
+
if (e.resultsCount !== undefined)
|
|
92
|
+
detail += ` → ${e.resultsCount} results`;
|
|
93
|
+
if (e.memoriesCount !== undefined)
|
|
94
|
+
detail += ` (${e.memoriesCount} memories)`;
|
|
95
|
+
if (e.error)
|
|
96
|
+
detail = chalk_1.default.red(e.error.substring(0, 50));
|
|
97
|
+
console.log(` ${icon} ${chalk_1.default.bold(op.padEnd(10))} ${dur.padEnd(8)} ${detail}`);
|
|
98
|
+
}
|
|
99
|
+
if (events.length > 15)
|
|
100
|
+
console.log(chalk_1.default.gray(` ... and ${events.length - 15} more`));
|
|
101
|
+
console.log('');
|
|
102
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -920,6 +920,15 @@ program
|
|
|
920
920
|
const { whyCommand } = await Promise.resolve().then(() => __importStar(require("./commands/why")));
|
|
921
921
|
whyCommand(query, opts);
|
|
922
922
|
});
|
|
923
|
+
// trickle memory
|
|
924
|
+
program
|
|
925
|
+
.command("memory")
|
|
926
|
+
.description("Show captured agent memory operations (Mem0 add/get/search/update/delete)")
|
|
927
|
+
.option("--json", "Output raw JSON")
|
|
928
|
+
.action(async (opts) => {
|
|
929
|
+
const { memoryCommand } = await Promise.resolve().then(() => __importStar(require("./commands/memory")));
|
|
930
|
+
memoryCommand(opts);
|
|
931
|
+
});
|
|
923
932
|
// trickle benchmark
|
|
924
933
|
program
|
|
925
934
|
.command("benchmark [command...]")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trickle-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.207",
|
|
4
4
|
"description": "Zero-code runtime observability for JS/Python + AI agent debugging. Traces LangChain, CrewAI, OpenAI, Anthropic, Gemini. Eval, security, compliance, cost tracking. Free, local-first.",
|
|
5
5
|
"keywords": ["observability", "tracing", "llm", "openai", "anthropic", "langchain", "crewai", "agent", "mcp", "debugging", "typescript", "python", "security", "eval", "compliance"],
|
|
6
6
|
"bin": {
|
|
@@ -281,12 +281,36 @@ export function costReportCommand(opts: { json?: boolean; budget?: string }): vo
|
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
// Provider-reported cache tokens (Anthropic cache_read/cache_creation)
|
|
285
|
+
const cacheReadTotal = calls.reduce((s: number, c: any) => s + (c.cacheReadTokens || 0), 0);
|
|
286
|
+
const cacheWriteTotal = calls.reduce((s: number, c: any) => s + (c.cacheWriteTokens || 0), 0);
|
|
287
|
+
const callsWithCache = calls.filter((c: any) => c.cacheReadTokens > 0 || c.cacheWriteTokens > 0);
|
|
288
|
+
|
|
289
|
+
if (callsWithCache.length > 0 || cacheDetected) {
|
|
285
290
|
console.log(chalk.gray('\n ' + '─'.repeat(60)));
|
|
286
|
-
console.log(chalk.bold(' Cache Analysis')
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
console.log(chalk.bold(' Cache Analysis'));
|
|
292
|
+
|
|
293
|
+
if (callsWithCache.length > 0) {
|
|
294
|
+
const cacheHitCalls = calls.filter((c: any) => c.cacheReadTokens > 0);
|
|
295
|
+
const hitRate = calls.length > 0 ? Math.round((cacheHitCalls.length / calls.length) * 100) : 0;
|
|
296
|
+
// Estimate savings: cached tokens cost ~90% less
|
|
297
|
+
const savedTokens = cacheReadTotal;
|
|
298
|
+
const avgInputPrice = totalCost > 0 && totalTokens > 0 ? (totalCost / totalTokens) : 0.000003;
|
|
299
|
+
const estimatedSavings = savedTokens * avgInputPrice * 0.9;
|
|
300
|
+
console.log(chalk.gray(' Provider-reported cache tokens:'));
|
|
301
|
+
console.log(` Hit rate: ${chalk.green(hitRate + '%')} (${cacheHitCalls.length}/${calls.length} calls used cache)`);
|
|
302
|
+
console.log(` Cache read: ${formatTokens(cacheReadTotal)} tokens | Cache write: ${formatTokens(cacheWriteTotal)} tokens`);
|
|
303
|
+
if (estimatedSavings > 0) {
|
|
304
|
+
console.log(` Estimated savings: ${chalk.green('~$' + estimatedSavings.toFixed(4))} from cached tokens`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (cacheDetected) {
|
|
309
|
+
console.log(chalk.gray(' Latency-based detection:'));
|
|
310
|
+
for (const ca of cacheAnalysis) {
|
|
311
|
+
const speedup = (ca.slowAvg / Math.max(1, ca.fastAvg)).toFixed(0);
|
|
312
|
+
console.log(` ${chalk.cyan(ca.model.padEnd(25))} hit rate: ${chalk.green(ca.hitRate + '%')} (${ca.fastCalls} fast, ${ca.slowCalls} slow) ${speedup}x speedup`);
|
|
313
|
+
}
|
|
290
314
|
}
|
|
291
315
|
}
|
|
292
316
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trickle memory — Show captured agent memory operations (Mem0).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
export function memoryCommand(opts: { json?: boolean }): void {
|
|
10
|
+
const dir = process.env.TRICKLE_LOCAL_DIR || path.join(process.cwd(), '.trickle');
|
|
11
|
+
const memFile = path.join(dir, 'memory.jsonl');
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(memFile)) {
|
|
14
|
+
console.log(chalk.yellow(' No memory data. Run an app that uses Mem0 with trickle.'));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const events = fs.readFileSync(memFile, 'utf-8').split('\n').filter(Boolean)
|
|
19
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
20
|
+
.filter((e: any) => e && e.kind === 'memory_op');
|
|
21
|
+
|
|
22
|
+
if (events.length === 0) {
|
|
23
|
+
console.log(chalk.yellow(' No memory operations captured.'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (opts.json) {
|
|
28
|
+
console.log(JSON.stringify(events, null, 2));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(chalk.bold(' trickle memory'));
|
|
34
|
+
console.log(chalk.gray(' ' + '─'.repeat(60)));
|
|
35
|
+
|
|
36
|
+
const adds = events.filter((e: any) => e.operation === 'add');
|
|
37
|
+
const searches = events.filter((e: any) => e.operation === 'search');
|
|
38
|
+
const gets = events.filter((e: any) => e.operation === 'get' || e.operation === 'get_all');
|
|
39
|
+
const updates = events.filter((e: any) => e.operation === 'update');
|
|
40
|
+
const deletes = events.filter((e: any) => e.operation === 'delete');
|
|
41
|
+
const errors = events.filter((e: any) => e.error);
|
|
42
|
+
|
|
43
|
+
console.log(` ${chalk.cyan(String(events.length))} operations: ${adds.length} add, ${searches.length} search, ${gets.length} get, ${updates.length} update, ${deletes.length} delete${errors.length > 0 ? chalk.red(`, ${errors.length} errors`) : ''}`);
|
|
44
|
+
console.log(chalk.gray(' ' + '─'.repeat(60)));
|
|
45
|
+
|
|
46
|
+
for (const e of events.slice(-15)) {
|
|
47
|
+
const op = (e as any).operation;
|
|
48
|
+
const dur = (e as any).durationMs ? chalk.gray(`${(e as any).durationMs}ms`) : '';
|
|
49
|
+
const icon = op === 'add' ? chalk.green('+') : op === 'search' ? chalk.blue('?') : op === 'delete' ? chalk.red('-') : op === 'update' ? chalk.yellow('~') : chalk.gray('→');
|
|
50
|
+
let detail = '';
|
|
51
|
+
if ((e as any).input) detail = (e as any).input.substring(0, 60);
|
|
52
|
+
if ((e as any).query) detail = `"${(e as any).query.substring(0, 50)}"`;
|
|
53
|
+
if ((e as any).memoryId) detail = `id:${(e as any).memoryId}`;
|
|
54
|
+
if ((e as any).resultsCount !== undefined) detail += ` → ${(e as any).resultsCount} results`;
|
|
55
|
+
if ((e as any).memoriesCount !== undefined) detail += ` (${(e as any).memoriesCount} memories)`;
|
|
56
|
+
if ((e as any).error) detail = chalk.red((e as any).error.substring(0, 50));
|
|
57
|
+
|
|
58
|
+
console.log(` ${icon} ${chalk.bold(op.padEnd(10))} ${dur.padEnd(8)} ${detail}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (events.length > 15) console.log(chalk.gray(` ... and ${events.length - 15} more`));
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -953,6 +953,16 @@ program
|
|
|
953
953
|
whyCommand(query, opts);
|
|
954
954
|
});
|
|
955
955
|
|
|
956
|
+
// trickle memory
|
|
957
|
+
program
|
|
958
|
+
.command("memory")
|
|
959
|
+
.description("Show captured agent memory operations (Mem0 add/get/search/update/delete)")
|
|
960
|
+
.option("--json", "Output raw JSON")
|
|
961
|
+
.action(async (opts) => {
|
|
962
|
+
const { memoryCommand } = await import("./commands/memory");
|
|
963
|
+
memoryCommand(opts);
|
|
964
|
+
});
|
|
965
|
+
|
|
956
966
|
// trickle benchmark
|
|
957
967
|
program
|
|
958
968
|
.command("benchmark [command...]")
|