Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
# OPENAI_TIMEOUT_MS alias for back-compat with v0.9.17 (precedence).
# AGENTMEMORY_LLM_TIMEOUT_MS=60000 # Default: 60 000 ms (60 s)

# AGENTMEMORY_GRAPH_MODEL= # Route mem::graph-extract to a
# # dedicated provider whose model
# # overrides the active provider
# # model. When unset, the active
# # provider model is used (current
# # behavior). Graph runs on its own
# # ResilientProvider instance, so
# # its concurrency limiter and
# # circuit breaker are independent
# # of summarize.

# Opt-in Claude-subscription fallback (spawns @anthropic-ai/claude-agent-sdk
# child sessions). Off by default — the agent-sdk fallback can trigger
# Stop-hook recursion (#149 follow-up) when invoked from inside Claude Code.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,7 @@ agentmemory auto-detects from your environment. By default, no LLM calls are mad
| Gemini | `GEMINI_API_KEY` | Also enables embeddings |
| OpenRouter | `OPENROUTER_API_KEY` | Any model |
| OpenAI API | `OPENAI_API_KEY` | Default `gpt-4o-mini`, override with `OPENAI_MODEL` |
| **Graph extraction (optional)** | `AGENTMEMORY_GRAPH_MODEL` | Routes `mem::graph-extract` to a dedicated provider whose model overrides the active provider model. Independent ResilientProvider, so its concurrency limiter and circuit breaker are not shared with summarize. |
| **Local (Ollama / LM Studio / vLLM / llama.cpp)** | `OPENAI_API_KEY=local` + `OPENAI_BASE_URL=http://localhost:11434/v1` (Ollama) or `http://localhost:1234/v1` (LM Studio) + `OPENAI_MODEL=<your model>` | Anything OpenAI-API-compatible. Zero cost, runs on your hardware. See [Local models](#local-models-ollama-lm-studio-vllm) below. |
| Claude subscription fallback | `AGENTMEMORY_ALLOW_AGENT_SDK=true` | Opt-in only. Spawns `@anthropic-ai/claude-agent-sdk` sessions — used to cause unbounded Stop-hook recursion (#149 follow-up) so it is no longer the default. |

Expand Down
9 changes: 9 additions & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ export function createFallbackProvider(
return new ResilientProvider(providers[0]);
}

// #blockers: graph extraction can run on a different model than summarize so a
// relation-native or smaller model can be paired with the heavier summary
// model. Falls back to the base model when AGENTMEMORY_GRAPH_MODEL is unset.
export function resolveGraphProviderConfig(
base: ProviderConfig,
): ProviderConfig {
return { ...base, model: getEnvVar("AGENTMEMORY_GRAPH_MODEL") || base.model };
}

function createBaseProvider(config: ProviderConfig): MemoryProvider {
switch (config.provider) {
case "minimax":
Expand Down
39 changes: 39 additions & 0 deletions test/graph-provider-routing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest";

import { resolveGraphProviderConfig } from "../src/providers/index.js";
import type { ProviderConfig } from "../src/types.js";

describe("resolveGraphProviderConfig", () => {
const ORIGINAL_ENV = { ...process.env };
const base: ProviderConfig = {
provider: "openai",
model: "base-model",
maxTokens: 4096,
baseURL: "http://example/v1",
};

beforeEach(() => {
delete process.env.AGENTMEMORY_GRAPH_MODEL;
});

afterEach(() => {
process.env = { ...ORIGINAL_ENV };
});

it("returns the base model when AGENTMEMORY_GRAPH_MODEL is unset", () => {
const result = resolveGraphProviderConfig(base);
expect(result.model).toBe("base-model");
expect(result.provider).toBe("openai");
expect(result.maxTokens).toBe(4096);
expect(result.baseURL).toBe("http://example/v1");
});

it("overrides only the model when AGENTMEMORY_GRAPH_MODEL is set", () => {
process.env.AGENTMEMORY_GRAPH_MODEL = "graph-model";
const result = resolveGraphProviderConfig(base);
expect(result.model).toBe("graph-model");
expect(result.provider).toBe("openai");
expect(result.maxTokens).toBe(4096);
expect(result.baseURL).toBe("http://example/v1");
});
});