@gamaze/hicortex 0.16.5 → 0.16.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.
package/dist/llm.d.ts CHANGED
@@ -31,6 +31,10 @@ export interface LlmConfig {
31
31
  enableThinking?: boolean;
32
32
  /** Context window for the ollama fast tier (completeOllama). Falls back to 2048. */
33
33
  numCtx?: number;
34
+ /** Flush ollama memory every N ollama calls (0 = off). See HicortexConfig.ollamaFlushEvery. */
35
+ ollamaFlushEvery?: number;
36
+ /** Ms to wait after an ollama flush for the runner to release. */
37
+ ollamaFlushWaitMs?: number;
34
38
  /** Optional separate endpoint for reflect-tier LLM (e.g. remote Ollama with larger model). */
35
39
  reflectBaseUrl?: string;
36
40
  reflectApiKey?: string;
@@ -212,6 +216,7 @@ export declare class RateLimitError extends Error {
212
216
  }
213
217
  export declare class LlmClient {
214
218
  private config;
219
+ private ollamaCallCount;
215
220
  constructor(config: LlmConfig);
216
221
  /** Endpoint identity for shared rate-limit state (provider + base URL). */
217
222
  private get endpointKey();
@@ -261,6 +266,14 @@ export declare class LlmClient {
261
266
  * Ollama: use /api/generate with think:false (important for qwen3.5 models).
262
267
  */
263
268
  private completeOllama;
269
+ /**
270
+ * Flush ollama's accumulated memory: unload the model (keep_alive:0) so the
271
+ * runner exits + releases its per-request RSS growth, then wait for the release
272
+ * before the next call reloads fresh. The runner takes >90 s to exit after
273
+ * keep_alive:0 (measured), so the wait is generous (ollamaFlushWaitMs, default
274
+ * 180 s). Best-effort — a flush failure just means no release this cycle.
275
+ */
276
+ private flushOllama;
264
277
  /**
265
278
  * Anthropic Messages API (/v1/messages).
266
279
  * Auth via x-api-key header.
package/dist/llm.js CHANGED
@@ -211,6 +211,12 @@ function applyTierTuningOverlay(llmConfig, savedConfig) {
211
211
  if (savedConfig.numCtx !== undefined) {
212
212
  llmConfig.numCtx = (0, config_read_js_1.readPositiveConfig)(savedConfig, "numCtx", 2048);
213
213
  }
214
+ if (savedConfig.ollamaFlushEvery !== undefined) {
215
+ llmConfig.ollamaFlushEvery = (0, config_read_js_1.readPositiveConfig)(savedConfig, "ollamaFlushEvery", 0);
216
+ }
217
+ if (savedConfig.ollamaFlushWaitMs !== undefined) {
218
+ llmConfig.ollamaFlushWaitMs = (0, config_read_js_1.readPositiveConfig)(savedConfig, "ollamaFlushWaitMs", 180000);
219
+ }
214
220
  }
215
221
  /**
216
222
  * Resolve an LlmConfig from a saved ~/.hicortex/config.json object.
@@ -511,6 +517,7 @@ exports.RateLimitError = RateLimitError;
511
517
  const rateLimitedUntilByEndpoint = new Map();
512
518
  class LlmClient {
513
519
  config;
520
+ ollamaCallCount = 0;
514
521
  constructor(config) {
515
522
  this.config = config;
516
523
  }
@@ -739,8 +746,43 @@ class LlmClient {
739
746
  catch { /* skip malformed lines */ }
740
747
  }
741
748
  }
749
+ // Flush ollama's accumulated memory every N calls. ollama's runner RSS grows
750
+ // ~171 MB/call and isn't freed between requests — on RAM-constrained boxes
751
+ // this swap-thrashes long consolidations. After the Nth call, unload the
752
+ // model (keep_alive:0) + wait for the runner to exit + release, so the next
753
+ // call reloads fresh (low RSS) instead of accumulating to thrash. Opt-in via
754
+ // ollamaFlushEvery (0 = off).
755
+ const flushEvery = this.config.ollamaFlushEvery ?? 0;
756
+ if (flushEvery > 0) {
757
+ this.ollamaCallCount++;
758
+ if (this.ollamaCallCount >= flushEvery) {
759
+ await this.flushOllama(model);
760
+ this.ollamaCallCount = 0;
761
+ }
762
+ }
742
763
  return result.trim();
743
764
  }
765
+ /**
766
+ * Flush ollama's accumulated memory: unload the model (keep_alive:0) so the
767
+ * runner exits + releases its per-request RSS growth, then wait for the release
768
+ * before the next call reloads fresh. The runner takes >90 s to exit after
769
+ * keep_alive:0 (measured), so the wait is generous (ollamaFlushWaitMs, default
770
+ * 180 s). Best-effort — a flush failure just means no release this cycle.
771
+ */
772
+ async flushOllama(model) {
773
+ const url = `${this.config.baseUrl.replace(/\/$/, "")}/api/generate`;
774
+ try {
775
+ await fetch(url, {
776
+ method: "POST",
777
+ headers: { "Content-Type": "application/json" },
778
+ body: JSON.stringify({ model, keep_alive: 0 }),
779
+ signal: AbortSignal.timeout(60_000),
780
+ });
781
+ }
782
+ catch { /* best-effort flush */ }
783
+ const waitMs = this.config.ollamaFlushWaitMs ?? 180_000;
784
+ await new Promise((r) => setTimeout(r, waitMs));
785
+ }
744
786
  /**
745
787
  * Anthropic Messages API (/v1/messages).
746
788
  * Auth via x-api-key header.
package/dist/types.d.ts CHANGED
@@ -313,6 +313,23 @@ export interface HicortexConfig {
313
313
  * scoring prompt actually needs more.
314
314
  */
315
315
  numCtx?: number;
316
+ /**
317
+ * Flush ollama's accumulated memory every N ollama calls — workaround for
318
+ * ollama's per-request memory growth (the runner's RSS climbs ~171 MB/call and
319
+ * isn't freed between requests), which swap-thrashes RAM-constrained boxes
320
+ * during long consolidations. Default 0 (off). When >0, every Nth ollama call
321
+ * triggers a `keep_alive:0` unload + an `ollamaFlushWaitMs` pause for the
322
+ * runner to exit + release, then the next call reloads fresh. N=15 caps a
323
+ * cycle at ~2.5 GB. Opt-in — set e.g. 15 on constrained boxes.
324
+ */
325
+ ollamaFlushEvery?: number;
326
+ /**
327
+ * Milliseconds to wait after an ollama flush (`keep_alive:0`) for the runner
328
+ * to exit + release its accumulated memory before the next call reloads.
329
+ * Default 180000 (3 min — the runner takes >90 s to exit after keep_alive:0;
330
+ * doubled for margin). Only relevant when `ollamaFlushEvery` > 0.
331
+ */
332
+ ollamaFlushWaitMs?: number;
316
333
  }
317
334
  /** A config-owned life-sphere domain (see HicortexConfig.domains). */
318
335
  export interface DomainDef {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.16.5",
3
+ "version": "0.16.6",
4
4
  "description": "Self-learning memory for AI agents — experience captured automatically, distilled into lessons overnight, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, and Pi.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {