From 1da2d4f320f256c1b127c5e369b021ff2a8ccbff Mon Sep 17 00:00:00 2001 From: David Gutowsky Date: Tue, 2 Jun 2026 07:27:06 -0500 Subject: [PATCH 1/5] fix: pin gpt-oss-120b to Cerebras, default provider to cerebras - Change DEFAULT_PROVIDER from "openrouter" to "cerebras" - Change DEFAULT_PROVIDER_ORDER from ["Groq", "Together", "Fireworks"] to ["Cerebras"] - Change OpenRouter provider routing from "order" (soft preference) to "only" (hard pin) so requests fail rather than silently falling through to non-Cerebras providers - Update module docstring to reflect new defaults --- src/lore/extraction/client.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lore/extraction/client.py b/src/lore/extraction/client.py index 64b02b0..b7647ed 100644 --- a/src/lore/extraction/client.py +++ b/src/lore/extraction/client.py @@ -7,8 +7,8 @@ Two OpenAI-compatible providers are supported, selected via the ``provider`` config key: -* ``openrouter`` (default) — Llama 3.1 8B routed through Groq/Together/Fireworks. -* ``cerebras`` — gpt-oss-120b on Cerebras Cloud (300+ TPS, 91-99% prompt cache). +* ``cerebras`` (default) — gpt-oss-120b on Cerebras Cloud (300+ TPS, 91-99% prompt cache). +* ``openrouter`` — Llama 3.1 8B routed exclusively through Cerebras via provider.only pin. Extraction is *best-effort*: every failure mode (missing API key, HTTP error, malformed JSON, unknown provider) is logged and converted to an empty result. @@ -32,10 +32,12 @@ OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions" CEREBRAS_URL = "https://api.cerebras.ai/v1/chat/completions" -DEFAULT_PROVIDER = "openrouter" +DEFAULT_PROVIDER = "cerebras" DEFAULT_MODEL = "meta-llama/llama-3.1-8b-instruct" CEREBRAS_DEFAULT_MODEL = "gpt-oss-120b" -DEFAULT_PROVIDER_ORDER = ["Groq", "Together", "Fireworks"] +# Default provider order for OpenRouter routing. Using "only" (not "order") for a hard pin +# so the request fails rather than silently routing to a different provider. +DEFAULT_PROVIDER_ORDER = ["Cerebras"] # Llama 3.1 8B is the largest model this pipeline uses on purpose — extraction # must stay fast and cheap. Do not raise this to a reasoning/larger model. _REQUEST_TIMEOUT = 30.0 @@ -44,7 +46,7 @@ class ExtractionClient: """Thin async wrapper over an OpenAI-compatible chat-completions endpoint. - Dispatches to OpenRouter (default) or Cerebras based on the ``provider`` + Dispatches to Cerebras (default) or OpenRouter based on the ``provider`` config key. """ @@ -118,7 +120,7 @@ def _build_request( } body = { "model": model, - "provider": {"order": list(provider_order)}, + "provider": {"only": list(provider_order)}, "messages": messages, "response_format": {"type": "json_object"}, "temperature": 0.1, From 21ec3689ddfa822840cc150295a48ad5784c37a9 Mon Sep 17 00:00:00 2001 From: David Gutowsky Date: Tue, 2 Jun 2026 07:27:19 -0500 Subject: [PATCH 2/5] docs: update auto-extraction setup to reflect Cerebras as default provider --- docs/auto-extraction-setup.md | 89 ++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/docs/auto-extraction-setup.md b/docs/auto-extraction-setup.md index 11098c3..61c336b 100644 --- a/docs/auto-extraction-setup.md +++ b/docs/auto-extraction-setup.md @@ -1,33 +1,37 @@ # Automatic Memory Extraction Setup Lore can automatically extract memorable facts from Hermes conversations using a fast -LLM (Llama 3.1 8B via OpenRouter) and store them in your knowledge base. +LLM and store them in your knowledge base. + +The default provider is **Cerebras** (direct). OpenRouter is also supported but requires +explicit configuration. ## Prerequisites - A Lore MCP server running with PostgreSQL backend -- An OpenRouter API key (free tier is sufficient) +- A Cerebras API key (recommended) **or** an OpenRouter API key -## Option A: OpenRouter +## Option A: Cerebras (default) -### 1. Get an OpenRouter API key +Cerebras Cloud runs on wafer-scale chips. With 300+ TPS and 91–99% prompt cache hit rate, +the static extraction system prompt is effectively free after the first call. -1. Sign up at https://openrouter.ai -2. Create a key at https://openrouter.ai/keys -3. Free tier: suitable for personal use (~$0.0001 per extraction call) +### 1. Get a Cerebras API key +1. Sign up at https://cloud.cerebras.ai +2. Create an API key -## 2. Set the environment variable +### 2. Set the environment variable ```bash -export OPENROUTER_API_KEY=sk-or-v1-... +export CEREBRAS_API_KEY=csk-... ``` Or add to your `.env` file: ``` -OPENROUTER_API_KEY=sk-or-v1-... +CEREBRAS_API_KEY=csk-... ``` -## 3. Enable in plugin config +### 3. Enable in plugin config In your `plugin.yaml` (or Hermes plugin config): @@ -35,45 +39,53 @@ In your `plugin.yaml` (or Hermes plugin config): config: auto_extract: enabled: true - model: meta-llama/llama-3.1-8b-instruct - provider_order: - - Groq # ~800 tok/s — fastest - - Together # fallback - - Fireworks # fallback - confidence_threshold: 0.75 # 0–1; higher = fewer but more reliable memories - dedup_similarity_threshold: 0.85 # cosine similarity for merge vs new entry - min_turns: 3 # skip single-exchange sessions + model: gpt-oss-120b # default; omit to use this automatically + confidence_threshold: 0.75 + dedup_similarity_threshold: 0.12 + min_turns: 3 ``` -## Option B: Cerebras (recommended if already using Cerebras for Hermes) +No `provider` key is needed — Cerebras is the default. -Cerebras Cloud runs on wafer-scale chips. With 300+ TPS and 91–99% prompt cache hit rate, -the static extraction system prompt is effectively free after the first call. +## Option B: OpenRouter (with Cerebras routing) -### Get a Cerebras API key -1. Sign up at https://cloud.cerebras.ai -2. Create an API key +If you are already using OpenRouter and prefer to route through it, set `provider: openrouter`. +The request will use `provider.only: ["Cerebras"]` — a hard pin that fails rather than +silently falling through to a different provider. + +### 1. Get an OpenRouter API key +1. Sign up at https://openrouter.ai +2. Create a key at https://openrouter.ai/keys + +### 2. Set the environment variable -### Set environment variable ```bash -export CEREBRAS_API_KEY=csk-... +export OPENROUTER_API_KEY=sk-or-v1-... ``` -### Configure +### 3. Enable in plugin config + ```yaml -auto_extract: - enabled: true - provider: cerebras - model: gpt-oss-120b - confidence_threshold: 0.75 - dedup_similarity_threshold: 0.12 - min_turns: 3 +config: + auto_extract: + enabled: true + provider: openrouter + model: meta-llama/llama-3.1-8b-instruct + provider_order: + - Cerebras # hard-pinned via "only" — will not fall through to other providers + confidence_threshold: 0.75 + dedup_similarity_threshold: 0.85 + min_turns: 3 ``` +Note: `provider_order` maps to `provider.only` in the OpenRouter request body, not +`provider.order`. This means the request will return an error rather than silently +routing to Groq, Together, or Fireworks if Cerebras is unavailable. + ## How it works At the end of each session, Lore asynchronously: -1. Sends the conversation turns to Llama 3.1 8B via OpenRouter (Groq backend) +1. Sends the conversation turns to gpt-oss-120b via Cerebras (or OpenRouter) 2. Extracts facts, preferences, goals, events, and system facts 3. Deduplicates against your existing KB using vector similarity 4. Writes new entries or updates existing ones @@ -117,5 +129,6 @@ to the agent until approved. ## Disabling -Set `enabled: false` or remove `OPENROUTER_API_KEY`. The plugin degrades gracefully — -manual `kb_add` continues to work as before. +Set `enabled: false` or remove the relevant API key (`CEREBRAS_API_KEY` or +`OPENROUTER_API_KEY`). The plugin degrades gracefully — manual `kb_add` continues to work +as before. From ee3264d573ae615f234eaee061e277f162973785 Mon Sep 17 00:00:00 2001 From: David Gutowsky Date: Tue, 2 Jun 2026 13:26:18 +0000 Subject: [PATCH 3/5] fix: keep openrouter as default provider, pin to Cerebras via provider.only Revert the DEFAULT_PROVIDER change back to openrouter. Lore extraction uses OpenRouter as the transport but hard-pins to Cerebras through provider.only (DEFAULT_PROVIDER_ORDER = ["Cerebras"]). The provider itself stays openrouter. Docstrings and docs/auto-extraction-setup.md updated to present OpenRouter (pinned to Cerebras) as the default and Cerebras-direct as the alternative, while preserving the only-vs-order explanation. Co-Authored-By: Claude Sonnet 4.6 --- docs/auto-extraction-setup.md | 67 ++++++++++++++++++----------------- src/lore/extraction/client.py | 11 +++--- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/docs/auto-extraction-setup.md b/docs/auto-extraction-setup.md index 61c336b..2a02dfa 100644 --- a/docs/auto-extraction-setup.md +++ b/docs/auto-extraction-setup.md @@ -3,32 +3,33 @@ Lore can automatically extract memorable facts from Hermes conversations using a fast LLM and store them in your knowledge base. -The default provider is **Cerebras** (direct). OpenRouter is also supported but requires -explicit configuration. +The default provider is **OpenRouter**, used as the transport but hard-pinned to Cerebras +via `provider_order: ["Cerebras"]` (which becomes `provider.only` in the request). Talking +to Cerebras Cloud directly is also supported as an alternative. ## Prerequisites - A Lore MCP server running with PostgreSQL backend -- A Cerebras API key (recommended) **or** an OpenRouter API key +- An OpenRouter API key (default) **or** a Cerebras API key -## Option A: Cerebras (default) +## Option A: OpenRouter (default, pinned to Cerebras) -Cerebras Cloud runs on wafer-scale chips. With 300+ TPS and 91–99% prompt cache hit rate, -the static extraction system prompt is effectively free after the first call. +OpenRouter is used as the transport, but the request is hard-pinned to Cerebras via +`provider.only` so it fails loudly rather than silently routing to a different backend. -### 1. Get a Cerebras API key -1. Sign up at https://cloud.cerebras.ai -2. Create an API key +### 1. Get an OpenRouter API key +1. Sign up at https://openrouter.ai +2. Create a key at https://openrouter.ai/keys ### 2. Set the environment variable ```bash -export CEREBRAS_API_KEY=csk-... +export OPENROUTER_API_KEY=sk-or-v1-... ``` Or add to your `.env` file: ``` -CEREBRAS_API_KEY=csk-... +OPENROUTER_API_KEY=sk-or-v1-... ``` ### 3. Enable in plugin config @@ -39,28 +40,36 @@ In your `plugin.yaml` (or Hermes plugin config): config: auto_extract: enabled: true - model: gpt-oss-120b # default; omit to use this automatically + model: meta-llama/llama-3.1-8b-instruct + provider_order: + - Cerebras # hard-pinned via "only" — will not fall through to other providers confidence_threshold: 0.75 - dedup_similarity_threshold: 0.12 + dedup_similarity_threshold: 0.85 min_turns: 3 ``` -No `provider` key is needed — Cerebras is the default. +No `provider` key is needed — `openrouter` is the default. The `provider_order` defaults +to `["Cerebras"]` if omitted. -## Option B: OpenRouter (with Cerebras routing) +Note: `provider_order` maps to `provider.only` in the OpenRouter request body, not +`provider.order`. This means the request will return an error rather than silently +routing to Groq, Together, or Fireworks if Cerebras is unavailable. -If you are already using OpenRouter and prefer to route through it, set `provider: openrouter`. -The request will use `provider.only: ["Cerebras"]` — a hard pin that fails rather than -silently falling through to a different provider. +## Option B: Cerebras (direct) -### 1. Get an OpenRouter API key -1. Sign up at https://openrouter.ai -2. Create a key at https://openrouter.ai/keys +If you prefer to talk to Cerebras Cloud directly instead of through OpenRouter, set +`provider: cerebras`. Cerebras Cloud runs on wafer-scale chips. With 300+ TPS and 91–99% +prompt cache hit rate, the static extraction system prompt is effectively free after the +first call. + +### 1. Get a Cerebras API key +1. Sign up at https://cloud.cerebras.ai +2. Create an API key ### 2. Set the environment variable ```bash -export OPENROUTER_API_KEY=sk-or-v1-... +export CEREBRAS_API_KEY=csk-... ``` ### 3. Enable in plugin config @@ -69,23 +78,17 @@ export OPENROUTER_API_KEY=sk-or-v1-... config: auto_extract: enabled: true - provider: openrouter - model: meta-llama/llama-3.1-8b-instruct - provider_order: - - Cerebras # hard-pinned via "only" — will not fall through to other providers + provider: cerebras + model: gpt-oss-120b confidence_threshold: 0.75 - dedup_similarity_threshold: 0.85 + dedup_similarity_threshold: 0.12 min_turns: 3 ``` -Note: `provider_order` maps to `provider.only` in the OpenRouter request body, not -`provider.order`. This means the request will return an error rather than silently -routing to Groq, Together, or Fireworks if Cerebras is unavailable. - ## How it works At the end of each session, Lore asynchronously: -1. Sends the conversation turns to gpt-oss-120b via Cerebras (or OpenRouter) +1. Sends the conversation turns to a fast model via OpenRouter pinned to Cerebras (or Cerebras direct) 2. Extracts facts, preferences, goals, events, and system facts 3. Deduplicates against your existing KB using vector similarity 4. Writes new entries or updates existing ones diff --git a/src/lore/extraction/client.py b/src/lore/extraction/client.py index b7647ed..77e4f4f 100644 --- a/src/lore/extraction/client.py +++ b/src/lore/extraction/client.py @@ -7,8 +7,9 @@ Two OpenAI-compatible providers are supported, selected via the ``provider`` config key: -* ``cerebras`` (default) — gpt-oss-120b on Cerebras Cloud (300+ TPS, 91-99% prompt cache). -* ``openrouter`` — Llama 3.1 8B routed exclusively through Cerebras via provider.only pin. +* ``openrouter`` (default) — used as the transport, but hard-pinned to Cerebras + via ``provider.only`` so requests fail loudly rather than silently routing elsewhere. +* ``cerebras`` — gpt-oss-120b on Cerebras Cloud directly (300+ TPS, 91-99% prompt cache). Extraction is *best-effort*: every failure mode (missing API key, HTTP error, malformed JSON, unknown provider) is logged and converted to an empty result. @@ -32,7 +33,7 @@ OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions" CEREBRAS_URL = "https://api.cerebras.ai/v1/chat/completions" -DEFAULT_PROVIDER = "cerebras" +DEFAULT_PROVIDER = "openrouter" DEFAULT_MODEL = "meta-llama/llama-3.1-8b-instruct" CEREBRAS_DEFAULT_MODEL = "gpt-oss-120b" # Default provider order for OpenRouter routing. Using "only" (not "order") for a hard pin @@ -46,8 +47,8 @@ class ExtractionClient: """Thin async wrapper over an OpenAI-compatible chat-completions endpoint. - Dispatches to Cerebras (default) or OpenRouter based on the ``provider`` - config key. + Dispatches to OpenRouter (default, hard-pinned to Cerebras via + ``provider.only``) or Cerebras directly, based on the ``provider`` config key. """ def __init__(self, config: dict[str, Any] | None = None): From 3d3a9f2ca034c6b4b1509cbbdd8e2b00c5861f44 Mon Sep 17 00:00:00 2001 From: David Gutowsky Date: Tue, 2 Jun 2026 14:44:48 +0000 Subject: [PATCH 4/5] fix(tests): update provider key assertions from 'order' to 'only' The OpenRouter request body now uses provider.only (hard-pin semantics) instead of provider.order, and the provider list changed from ["Groq", "Together", "Fireworks"] to ["Cerebras"]. Update both test_provider_order_in_request_body and test_openrouter_still_has_provider_routing to match the new key and value. Co-Authored-By: Claude Sonnet 4.6 --- tests/test_extraction_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_extraction_client.py b/tests/test_extraction_client.py index ba8de7f..ec1d866 100644 --- a/tests/test_extraction_client.py +++ b/tests/test_extraction_client.py @@ -128,7 +128,7 @@ def test_provider_order_in_request_body(monkeypatch): response = _FakeResponse(_valid_payload('{"memories": []}')) with _patch_client(response, recorder): asyncio.run(ExtractionClient().extract("[user]: hi\n", {})) - assert recorder["body"]["provider"]["order"] == ["Groq", "Together", "Fireworks"] + assert recorder["body"]["provider"]["only"] == ["Cerebras"] def test_model_in_request_body(monkeypatch): @@ -188,4 +188,4 @@ def test_openrouter_still_has_provider_routing(monkeypatch): ExtractionClient().extract("[user]: hi\n", {"auto_extract": {"provider": "openrouter"}}) ) assert recorder["url"] == "https://openrouter.ai/api/v1/chat/completions" - assert recorder["body"]["provider"]["order"] == ["Groq", "Together", "Fireworks"] + assert recorder["body"]["provider"]["only"] == ["Cerebras"] From 50553317c0393dd491a976e0c1a1f223eaded59c Mon Sep 17 00:00:00 2001 From: David Gutowsky Date: Tue, 2 Jun 2026 09:46:02 -0500 Subject: [PATCH 5/5] fix: silence Pyright unused-variable warnings in test_extraction_client - __aexit__(self, *exc) -> *_ (exc tuple never accessed) - _factory(*args, **kwargs) -> *_args, **_kwargs (mock stub ignores params) --- tests/test_extraction_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_extraction_client.py b/tests/test_extraction_client.py index ec1d866..8d43f53 100644 --- a/tests/test_extraction_client.py +++ b/tests/test_extraction_client.py @@ -46,7 +46,7 @@ def __init__(self, response, recorder): async def __aenter__(self): return self - async def __aexit__(self, *exc): + async def __aexit__(self, *_): return False async def post(self, url, *, headers=None, json=None): # noqa: A002 @@ -57,7 +57,7 @@ async def post(self, url, *, headers=None, json=None): # noqa: A002 def _patch_client(response, recorder): - def _factory(*args, **kwargs): + def _factory(*_args, **_kwargs): return _FakeAsyncClient(response, recorder) return mock.patch.object(httpx, "AsyncClient", _factory)