@lotargo/memory_plugin 1.1.5 → 1.1.6

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.
@@ -1,72 +1,72 @@
1
- import { readFile, writeFile, mkdir } from "fs/promises";
2
- import { existsSync } from "fs";
3
- import { join, basename } from "path";
4
- import { homedir } from "os";
5
-
6
- function resolveMemoryDir() {
7
- if (process.env.MEMORY_DIR) return process.env.MEMORY_DIR;
8
- if (process.env.OPENCODE_CONFIG_DIR) return join(process.env.OPENCODE_CONFIG_DIR, "memory");
9
-
10
- const legacyDir = join(homedir(), ".config", "opencode", "memory");
11
- if (existsSync(legacyDir)) {
12
- return legacyDir;
13
- }
14
-
15
- if (process.platform === "win32") {
16
- const appData = process.env.LOCALAPPDATA || process.env.APPDATA || join(homedir(), "AppData", "Local");
17
- return join(appData, "opencode", "memory");
18
- }
19
-
20
- const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
21
- return join(configHome, "opencode", "memory");
22
- }
23
-
24
- export const MEMORY_DIR = resolveMemoryDir();
25
- export const GLOBAL_KEY = "global";
26
-
27
- export async function ensureDir() {
28
- if (!existsSync(MEMORY_DIR)) await mkdir(MEMORY_DIR, { recursive: true });
29
- const storageDir = join(MEMORY_DIR, "storage");
30
- const blobsDir = join(storageDir, "blobs");
31
- const modelsDir = join(storageDir, "models");
32
- const exportsDir = join(MEMORY_DIR, "exports");
33
- if (!existsSync(blobsDir)) await mkdir(blobsDir, { recursive: true });
34
- if (!existsSync(modelsDir)) await mkdir(modelsDir, { recursive: true });
35
- if (!existsSync(exportsDir)) await mkdir(exportsDir, { recursive: true });
36
- }
37
-
38
- export function projectName(worktree, directory) {
39
- const dir = worktree || directory || process.cwd();
40
- return dir ? basename(dir) : "default";
41
- }
42
-
43
- export function scopeKey(scope, worktree, directory) {
44
- return scope === "global" ? GLOBAL_KEY : projectName(worktree, directory);
45
- }
46
-
47
- function memoryPath(key) {
48
- return join(MEMORY_DIR, `${key.replace(/[^a-zA-Z0-9_-]/g, "_")}.md`);
49
- }
50
-
51
- export async function readMemory(key) {
52
- const fp = memoryPath(key);
53
- if (!existsSync(fp)) return [];
54
- const content = await readFile(fp, "utf-8");
55
- return content.split("\n").filter((l) => l.startsWith("- ["));
56
- }
57
-
58
- export async function readMemoryRaw(key) {
59
- return (await readMemory(key)).map((e) => e.slice(2));
60
- }
61
-
62
- export async function writeMemory(key, entries) {
63
- const header = `# ${key === GLOBAL_KEY ? "Global Memory" : `Memory: ${key}`}\n\n`;
64
- await writeFile(memoryPath(key), header + entries.join("\n") + "\n");
65
- }
66
-
67
- export function today() {
68
- const d = new Date();
69
- const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
70
- const time = `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
71
- return `${date} ${time}`;
72
- }
1
+ import { readFile, writeFile, mkdir } from "fs/promises";
2
+ import { existsSync } from "fs";
3
+ import { join, basename } from "path";
4
+ import { homedir } from "os";
5
+
6
+ function resolveMemoryDir() {
7
+ if (process.env.MEMORY_DIR) return process.env.MEMORY_DIR;
8
+ if (process.env.OPENCODE_CONFIG_DIR) return join(process.env.OPENCODE_CONFIG_DIR, "memory");
9
+
10
+ const legacyDir = join(homedir(), ".config", "opencode", "memory");
11
+ if (existsSync(legacyDir)) {
12
+ return legacyDir;
13
+ }
14
+
15
+ if (process.platform === "win32") {
16
+ const appData = process.env.LOCALAPPDATA || process.env.APPDATA || join(homedir(), "AppData", "Local");
17
+ return join(appData, "opencode", "memory");
18
+ }
19
+
20
+ const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
21
+ return join(configHome, "opencode", "memory");
22
+ }
23
+
24
+ export const MEMORY_DIR = resolveMemoryDir();
25
+ export const GLOBAL_KEY = "global";
26
+
27
+ export async function ensureDir() {
28
+ if (!existsSync(MEMORY_DIR)) await mkdir(MEMORY_DIR, { recursive: true });
29
+ const storageDir = join(MEMORY_DIR, "storage");
30
+ const blobsDir = join(storageDir, "blobs");
31
+ const modelsDir = join(storageDir, "models");
32
+ const exportsDir = join(MEMORY_DIR, "exports");
33
+ if (!existsSync(blobsDir)) await mkdir(blobsDir, { recursive: true });
34
+ if (!existsSync(modelsDir)) await mkdir(modelsDir, { recursive: true });
35
+ if (!existsSync(exportsDir)) await mkdir(exportsDir, { recursive: true });
36
+ }
37
+
38
+ export function projectName(worktree, directory) {
39
+ const dir = worktree || directory || process.cwd();
40
+ return dir ? basename(dir) : "default";
41
+ }
42
+
43
+ export function scopeKey(scope, worktree, directory) {
44
+ return scope === "global" ? GLOBAL_KEY : projectName(worktree, directory);
45
+ }
46
+
47
+ function memoryPath(key) {
48
+ return join(MEMORY_DIR, `${key.replace(/[^a-zA-Z0-9_-]/g, "_")}.md`);
49
+ }
50
+
51
+ export async function readMemory(key) {
52
+ const fp = memoryPath(key);
53
+ if (!existsSync(fp)) return [];
54
+ const content = await readFile(fp, "utf-8");
55
+ return content.split("\n").filter((l) => l.startsWith("- ["));
56
+ }
57
+
58
+ export async function readMemoryRaw(key) {
59
+ return (await readMemory(key)).map((e) => e.slice(2));
60
+ }
61
+
62
+ export async function writeMemory(key, entries) {
63
+ const header = `# ${key === GLOBAL_KEY ? "Global Memory" : `Memory: ${key}`}\n\n`;
64
+ await writeFile(memoryPath(key), header + entries.join("\n") + "\n");
65
+ }
66
+
67
+ export function today() {
68
+ const d = new Date();
69
+ const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
70
+ const time = `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
71
+ return `${date} ${time}`;
72
+ }
@@ -0,0 +1,166 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const execFileAsync = promisify(execFile);
5
+
6
+ export async function getGpuUtilizationAsync() {
7
+ try {
8
+ const { stdout } = await execFileAsync("nvidia-smi",
9
+ ["--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"],
10
+ { timeout: 1000 }
11
+ );
12
+ const val = parseInt(stdout.trim(), 10);
13
+ if (!isNaN(val)) return val;
14
+ } catch {}
15
+
16
+ if (process.platform === "win32") {
17
+ try {
18
+ const { stdout } = await execFileAsync("powershell",
19
+ ["-NoProfile", "-Command", "(Get-CimInstance Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine | Measure-Object -Property UtilizationPercentage -Sum).Sum"],
20
+ { timeout: 1500 }
21
+ );
22
+ const val = parseInt(stdout.trim(), 10);
23
+ if (!isNaN(val)) return Math.min(100, val);
24
+ } catch {}
25
+ }
26
+
27
+ return null;
28
+ }
29
+
30
+ export class GpuMonitor {
31
+ constructor(sampleIntervalMs = 100) {
32
+ this.intervalMs = sampleIntervalMs;
33
+ this.samples = [];
34
+ this.timer = null;
35
+ this.isMonitoring = false;
36
+ }
37
+
38
+ start() {
39
+ this.samples = [];
40
+ this.isMonitoring = true;
41
+ this.sample();
42
+
43
+ this.timer = setInterval(() => {
44
+ if (this.isMonitoring) {
45
+ this.sample();
46
+ }
47
+ }, this.intervalMs);
48
+ }
49
+
50
+ async sample() {
51
+ const util = await getGpuUtilizationAsync();
52
+ if (util !== null) {
53
+ this.samples.push(util);
54
+ }
55
+ }
56
+
57
+ stop() {
58
+ this.isMonitoring = false;
59
+ if (this.timer) {
60
+ clearInterval(this.timer);
61
+ this.timer = null;
62
+ }
63
+
64
+ if (this.samples.length === 0) {
65
+ return { avg: null, peak: null, samplesCount: 0 };
66
+ }
67
+
68
+ const max = Math.max(...this.samples);
69
+ const sum = this.samples.reduce((a, b) => a + b, 0);
70
+ const avg = Math.round(sum / this.samples.length);
71
+
72
+ return {
73
+ avg,
74
+ peak: max,
75
+ samplesCount: this.samples.length,
76
+ samples: this.samples,
77
+ };
78
+ }
79
+ }
80
+
81
+ export class ExecutionTracer {
82
+ constructor(name = "Batch Inference") {
83
+ this.name = name;
84
+ this.stages = [];
85
+ this.currentStage = null;
86
+ this.startTime = Date.now();
87
+ }
88
+
89
+ startStage(stageName, category = "CPU") {
90
+ const now = performance.now();
91
+ if (this.currentStage) {
92
+ this.currentStage.duration = now - this.currentStage.startTime;
93
+ this.stages.push(this.currentStage);
94
+ }
95
+ this.currentStage = {
96
+ name: stageName,
97
+ category, // "CPU" or "GPU"
98
+ startTime: now,
99
+ duration: 0,
100
+ };
101
+ }
102
+
103
+ endStage() {
104
+ const now = performance.now();
105
+ if (this.currentStage) {
106
+ this.currentStage.duration = now - this.currentStage.startTime;
107
+ this.stages.push(this.currentStage);
108
+ this.currentStage = null;
109
+ }
110
+ }
111
+
112
+ getSummary(gpuStats = null) {
113
+ this.endStage();
114
+ const totalMs = this.stages.reduce((sum, s) => sum + s.duration, 0);
115
+
116
+ const breakdown = this.stages.map((s) => ({
117
+ name: s.name,
118
+ category: s.category,
119
+ durationMs: parseFloat(s.duration.toFixed(2)),
120
+ pct: totalMs > 0 ? parseFloat(((s.duration / totalMs) * 100).toFixed(1)) : 0,
121
+ }));
122
+
123
+ const cpuTime = this.stages.filter((s) => s.category === "CPU").reduce((sum, s) => sum + s.duration, 0);
124
+ const gpuTime = this.stages.filter((s) => s.category === "GPU").reduce((sum, s) => sum + s.duration, 0);
125
+
126
+ return {
127
+ name: this.name,
128
+ totalMs: parseFloat(totalMs.toFixed(2)),
129
+ cpuMs: parseFloat(cpuTime.toFixed(2)),
130
+ gpuMs: parseFloat(gpuTime.toFixed(2)),
131
+ cpuPct: totalMs > 0 ? parseFloat(((cpuTime / totalMs) * 100).toFixed(1)) : 0,
132
+ gpuPct: totalMs > 0 ? parseFloat(((gpuTime / totalMs) * 100).toFixed(1)) : 0,
133
+ breakdown,
134
+ gpuStats,
135
+ };
136
+ }
137
+
138
+ printTraceReport(gpuStats = null, minGpuThreshold = 0) {
139
+ const summary = this.getSummary(gpuStats);
140
+ const line = "─".repeat(65);
141
+
142
+ console.log(`\n┌${line}┐`);
143
+ console.log(`│ CPU/GPU OPERATION TRACE & BOTTLENECK PROFILE: ${summary.name.padEnd(16)} │`);
144
+ console.log(`├${line}┤`);
145
+
146
+ for (const b of summary.breakdown) {
147
+ const icon = b.category === "GPU" ? "⚡ [GPU]" : "💻 [CPU]";
148
+ const label = `${icon} ${b.name}`.padEnd(42);
149
+ const timeStr = `${b.durationMs.toFixed(1)}ms (${b.pct.toFixed(1)}%)`.padStart(18);
150
+ console.log(`│ ${label}${timeStr} │`);
151
+ }
152
+
153
+ console.log(`├${line}┤`);
154
+ console.log(`│ Total Batch Time: ${summary.totalMs.toFixed(1)}ms | CPU Time: ${summary.cpuMs.toFixed(1)}ms (${summary.cpuPct}%) | GPU Engine Time: ${summary.gpuMs.toFixed(1)}ms (${summary.gpuPct}%) │`);
155
+
156
+ if (gpuStats && gpuStats.peak !== null) {
157
+ const statsBadge = `Peak: ${gpuStats.peak}%, Avg: ${gpuStats.avg}%`;
158
+ console.log(`│ GPU Hardware Load: ${statsBadge.padEnd(44)} │`);
159
+ console.log(`└${line}┘\n`);
160
+ } else {
161
+ console.log(`└${line}┘\n`);
162
+ }
163
+
164
+ return summary;
165
+ }
166
+ }