memkernel 0.4.0__tar.gz → 0.4.1__tar.gz
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.
- {memkernel-0.4.0 → memkernel-0.4.1}/PKG-INFO +23 -3
- {memkernel-0.4.0 → memkernel-0.4.1}/README.md +22 -2
- {memkernel-0.4.0 → memkernel-0.4.1}/pyproject.toml +2 -1
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/__init__.py +1 -1
- memkernel-0.4.1/src/memkernel/config.py +129 -0
- memkernel-0.4.0/src/memkernel/config.py +0 -70
- {memkernel-0.4.0 → memkernel-0.4.1}/LICENSE +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/NOTICE +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/adapters/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/adapters/rlmstudio.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/api/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/api/app.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/api/routes/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/api/routes/admin.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/core/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/core/models.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/core/ports.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/core/services.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/factory.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/lifecycle.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/mcp/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/mcp/server.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/mcp/tools/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/mcp/tools/memory.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/mcp/tools/wiki.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/memory/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/memory/dedup.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/memory/lifecycle.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/memory/types.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/py.typed +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/chunking.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/embeddings.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/reasoner.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/search.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/retrieval/sparse.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/storage/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/storage/chroma.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/storage/documents.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/wiki/__init__.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/wiki/pages.py +0 -0
- {memkernel-0.4.0 → memkernel-0.4.1}/src/memkernel/wiki/sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memkernel
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Code-aware memory server for AI coding copilots
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -47,6 +47,24 @@ MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory a
|
|
|
47
47
|
|
|
48
48
|
**Tiered memory (0.2)**: memories live in three tiers with different persistence rules — *working* (the current task, hours), *short-term* (episodes and observations, decays unless recalled) and *long-term* (decisions, conventions, facts, procedures, code; never decays, replaced by supersession). Recall ranks by relevance, recency and importance; `consolidate` and `promote` move what proved useful into long-term memory; an LLM-wiki bridge keeps a human-readable, git-versioned copy of long-term knowledge. Design: [`docs/architecture/memory-tiers.md`](docs/architecture/memory-tiers.md).
|
|
49
49
|
|
|
50
|
+
## What MemKernel is, and what it is not
|
|
51
|
+
|
|
52
|
+
**It is:**
|
|
53
|
+
|
|
54
|
+
- **Persistent memory for a coding agent.** What the agent retains survives context compaction and the end of a session, in a local ChromaDB store per project.
|
|
55
|
+
- **Explicit.** The agent decides what to retain (decisions, conventions, facts, code snapshots, tasks) and what to recall. The lifecycle hooks only automate the protocol around that: checkpoint before compaction, recovery after it, consolidation and revalidation at session start.
|
|
56
|
+
- **Code-aware.** AST chunking, hybrid search that finds identifiers as well as meaning, and revalidation of `code` memories against the files they came from.
|
|
57
|
+
- **No mandatory generative LLM.** Default retrieval uses a local sentence-transformer embedding model plus BM25 and RRF; nothing generates text inside MemKernel. `ask` with the built-in reasoner returns the best-matching chunks; synthesis is the agent's job, or an optional external reasoner's.
|
|
58
|
+
|
|
59
|
+
**It is not:**
|
|
60
|
+
|
|
61
|
+
- **A token optimizer or context compressor.** MemKernel does not shrink the context window, compress prompts, or lower model cost by itself. It adds tool calls and returned content to a session. Any token saving comes from the agent re-reading fewer files or documents after a resume; that is a side effect to measure, not a promise.
|
|
62
|
+
- **A replacement for files, specs and git.** Progress notes, task lists and review records already carry state between sessions. MemKernel adds retrieval by meaning and identifier, tiers with decay, supersession and revalidation on top. When a file already holds everything the next session needs, expect no gain.
|
|
63
|
+
- **An index of the whole repository.** `code` memories are the snapshots the agent chose to retain, not an automatic scan of every file.
|
|
64
|
+
- **A cross-project knowledge base.** One store per project id. Sharing is deliberate: a second server with a shared id, or the wiki bridge.
|
|
65
|
+
|
|
66
|
+
**Evidence.** `eval/run_eval.py` measures retrieval (recall@k, precision@k, MRR). An agent-level benchmark that runs the same agent with and without MemKernel, including a negative control where files already carry all state, is tracked in [#17](https://github.com/gosha70/memkernel/issues/17). Results will be published per minor release, including the cases with no gain.
|
|
67
|
+
|
|
50
68
|
---
|
|
51
69
|
|
|
52
70
|
## Requirements
|
|
@@ -221,6 +239,8 @@ ask(ref_id="<document ref_id>", question="Which endpoints need rate limiting?",
|
|
|
221
239
|
## Configuration
|
|
222
240
|
|
|
223
241
|
All settings are read from environment variables with the `MEMKERNEL_` prefix, or from a `.env` file.
|
|
242
|
+
Entries in `.env` without the prefix (keys for other tools) are ignored; a `MEMKERNEL_` key that matches no
|
|
243
|
+
setting is logged as a warning at startup.
|
|
224
244
|
|
|
225
245
|
| Variable | Default | Description |
|
|
226
246
|
|----------|---------|-------------|
|
|
@@ -331,7 +351,7 @@ Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](ht
|
|
|
331
351
|
- `wiki_export` compiles every long-term memory that has no page yet into a **draft** page under `decisions/`, `concepts/` or `workflows/`, with the required sections and a `memkernel://<project>/<ref_id>` citation. Existing pages are never overwritten.
|
|
332
352
|
- `wiki_import` indexes curated pages as long-term memories so `recall` finds the reviewed version; a changed page supersedes its earlier memory.
|
|
333
353
|
|
|
334
|
-
Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools.
|
|
354
|
+
Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools. The standalone maintainer that owns this page schema is [wiki-studio](https://github.com/gosha70/wiki-studio) (`pip install wiki-studio`; `wiki-studio lint --health` checks what `wiki_export` writes). Rationale: [`docs/adr/ADR-008-llm-wiki-as-semantic-layer.md`](docs/adr/ADR-008-llm-wiki-as-semantic-layer.md).
|
|
335
355
|
|
|
336
356
|
### Revalidation and store health
|
|
337
357
|
|
|
@@ -411,7 +431,7 @@ MemKernel is the memory component of a small family of projects by the same auth
|
|
|
411
431
|
|
|
412
432
|
| Project | Role | Relationship |
|
|
413
433
|
|---|---|---|
|
|
414
|
-
| [
|
|
434
|
+
| [wiki-studio](https://github.com/gosha70/wiki-studio) | LLM-wiki maintainer (ingest / promote / query / lint / audit), stdlib-only | the human-readable form of long-term memory; `wiki_export` / `wiki_import` speak its page schema (ADR-008), and CI lints exporter output with it |
|
|
415
435
|
| [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer behind `document` memories and the `ask()` tool (`MEMKERNEL_REASONER=rlmstudio`, ADR-009) |
|
|
416
436
|
| [code-copilot-team](https://github.com/gosha70/code-copilot-team) | Enforceable harness configuration for Claude Code, Pi and other copilots | consumes MemKernel through MCP and hooks |
|
|
417
437
|
| [haa](https://github.com/gosha70/haa) | Home-built AI coding harness, future component assembler | consumes MemKernel in-process through `build_service()` |
|
|
@@ -18,6 +18,24 @@ MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory a
|
|
|
18
18
|
|
|
19
19
|
**Tiered memory (0.2)**: memories live in three tiers with different persistence rules — *working* (the current task, hours), *short-term* (episodes and observations, decays unless recalled) and *long-term* (decisions, conventions, facts, procedures, code; never decays, replaced by supersession). Recall ranks by relevance, recency and importance; `consolidate` and `promote` move what proved useful into long-term memory; an LLM-wiki bridge keeps a human-readable, git-versioned copy of long-term knowledge. Design: [`docs/architecture/memory-tiers.md`](docs/architecture/memory-tiers.md).
|
|
20
20
|
|
|
21
|
+
## What MemKernel is, and what it is not
|
|
22
|
+
|
|
23
|
+
**It is:**
|
|
24
|
+
|
|
25
|
+
- **Persistent memory for a coding agent.** What the agent retains survives context compaction and the end of a session, in a local ChromaDB store per project.
|
|
26
|
+
- **Explicit.** The agent decides what to retain (decisions, conventions, facts, code snapshots, tasks) and what to recall. The lifecycle hooks only automate the protocol around that: checkpoint before compaction, recovery after it, consolidation and revalidation at session start.
|
|
27
|
+
- **Code-aware.** AST chunking, hybrid search that finds identifiers as well as meaning, and revalidation of `code` memories against the files they came from.
|
|
28
|
+
- **No mandatory generative LLM.** Default retrieval uses a local sentence-transformer embedding model plus BM25 and RRF; nothing generates text inside MemKernel. `ask` with the built-in reasoner returns the best-matching chunks; synthesis is the agent's job, or an optional external reasoner's.
|
|
29
|
+
|
|
30
|
+
**It is not:**
|
|
31
|
+
|
|
32
|
+
- **A token optimizer or context compressor.** MemKernel does not shrink the context window, compress prompts, or lower model cost by itself. It adds tool calls and returned content to a session. Any token saving comes from the agent re-reading fewer files or documents after a resume; that is a side effect to measure, not a promise.
|
|
33
|
+
- **A replacement for files, specs and git.** Progress notes, task lists and review records already carry state between sessions. MemKernel adds retrieval by meaning and identifier, tiers with decay, supersession and revalidation on top. When a file already holds everything the next session needs, expect no gain.
|
|
34
|
+
- **An index of the whole repository.** `code` memories are the snapshots the agent chose to retain, not an automatic scan of every file.
|
|
35
|
+
- **A cross-project knowledge base.** One store per project id. Sharing is deliberate: a second server with a shared id, or the wiki bridge.
|
|
36
|
+
|
|
37
|
+
**Evidence.** `eval/run_eval.py` measures retrieval (recall@k, precision@k, MRR). An agent-level benchmark that runs the same agent with and without MemKernel, including a negative control where files already carry all state, is tracked in [#17](https://github.com/gosha70/memkernel/issues/17). Results will be published per minor release, including the cases with no gain.
|
|
38
|
+
|
|
21
39
|
---
|
|
22
40
|
|
|
23
41
|
## Requirements
|
|
@@ -192,6 +210,8 @@ ask(ref_id="<document ref_id>", question="Which endpoints need rate limiting?",
|
|
|
192
210
|
## Configuration
|
|
193
211
|
|
|
194
212
|
All settings are read from environment variables with the `MEMKERNEL_` prefix, or from a `.env` file.
|
|
213
|
+
Entries in `.env` without the prefix (keys for other tools) are ignored; a `MEMKERNEL_` key that matches no
|
|
214
|
+
setting is logged as a warning at startup.
|
|
195
215
|
|
|
196
216
|
| Variable | Default | Description |
|
|
197
217
|
|----------|---------|-------------|
|
|
@@ -302,7 +322,7 @@ Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](ht
|
|
|
302
322
|
- `wiki_export` compiles every long-term memory that has no page yet into a **draft** page under `decisions/`, `concepts/` or `workflows/`, with the required sections and a `memkernel://<project>/<ref_id>` citation. Existing pages are never overwritten.
|
|
303
323
|
- `wiki_import` indexes curated pages as long-term memories so `recall` finds the reviewed version; a changed page supersedes its earlier memory.
|
|
304
324
|
|
|
305
|
-
Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools.
|
|
325
|
+
Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools. The standalone maintainer that owns this page schema is [wiki-studio](https://github.com/gosha70/wiki-studio) (`pip install wiki-studio`; `wiki-studio lint --health` checks what `wiki_export` writes). Rationale: [`docs/adr/ADR-008-llm-wiki-as-semantic-layer.md`](docs/adr/ADR-008-llm-wiki-as-semantic-layer.md).
|
|
306
326
|
|
|
307
327
|
### Revalidation and store health
|
|
308
328
|
|
|
@@ -382,7 +402,7 @@ MemKernel is the memory component of a small family of projects by the same auth
|
|
|
382
402
|
|
|
383
403
|
| Project | Role | Relationship |
|
|
384
404
|
|---|---|---|
|
|
385
|
-
| [
|
|
405
|
+
| [wiki-studio](https://github.com/gosha70/wiki-studio) | LLM-wiki maintainer (ingest / promote / query / lint / audit), stdlib-only | the human-readable form of long-term memory; `wiki_export` / `wiki_import` speak its page schema (ADR-008), and CI lints exporter output with it |
|
|
386
406
|
| [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer behind `document` memories and the `ask()` tool (`MEMKERNEL_REASONER=rlmstudio`, ADR-009) |
|
|
387
407
|
| [code-copilot-team](https://github.com/gosha70/code-copilot-team) | Enforceable harness configuration for Claude Code, Pi and other copilots | consumes MemKernel through MCP and hooks |
|
|
388
408
|
| [haa](https://github.com/gosha70/haa) | Home-built AI coding harness, future component assembler | consumes MemKernel in-process through `build_service()` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "memkernel"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.1"
|
|
4
4
|
description = "Code-aware memory server for AI coding copilots"
|
|
5
5
|
authors = ["EGOGE"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -40,6 +40,7 @@ ruff = ">=0.9"
|
|
|
40
40
|
mypy = ">=1.13"
|
|
41
41
|
httpx = ">=0.27"
|
|
42
42
|
pip-audit = "^2.10.0"
|
|
43
|
+
wiki-studio = ">=0.1,<1"
|
|
43
44
|
|
|
44
45
|
[build-system]
|
|
45
46
|
requires = ["poetry-core"]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Copyright (c) 2026 EGOGE
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""MemKernel configuration via environment variables."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
9
|
+
|
|
10
|
+
from pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from pydantic.fields import FieldInfo
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
ENV_PREFIX = "MEMKERNEL_"
|
|
18
|
+
ENV_FILE = ".env"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MemKernelSettings(BaseSettings):
|
|
22
|
+
"""All settings are configurable via MEMKERNEL_ prefixed env vars."""
|
|
23
|
+
|
|
24
|
+
# Unknown keys stay forbidden (constructor typos raise), but the .env source
|
|
25
|
+
# is filtered to known settings: a project .env usually holds keys for other
|
|
26
|
+
# tools and must not stop the server from starting (issue #15). Misspelled
|
|
27
|
+
# MEMKERNEL_ keys are reported by settings_customise_sources().
|
|
28
|
+
model_config = SettingsConfigDict(env_prefix=ENV_PREFIX, env_file=ENV_FILE)
|
|
29
|
+
|
|
30
|
+
project_id: str = "default"
|
|
31
|
+
default_session_id: str = "default"
|
|
32
|
+
|
|
33
|
+
# ChromaDB
|
|
34
|
+
chroma_persist_path: str = ".memkernel/chroma"
|
|
35
|
+
chroma_collection_prefix: str = "memkernel"
|
|
36
|
+
|
|
37
|
+
# Embedding
|
|
38
|
+
embedding_model: str = "all-MiniLM-L6-v2"
|
|
39
|
+
embedding_dimension: int = 384
|
|
40
|
+
|
|
41
|
+
# Retrieval
|
|
42
|
+
search_top_k: int = 5
|
|
43
|
+
rrf_k: int = 60
|
|
44
|
+
preview_length: int = 200
|
|
45
|
+
|
|
46
|
+
# Chunking
|
|
47
|
+
chunk_size: int = 1500 # max chars per chunk
|
|
48
|
+
chunk_overlap: int = 200 # char overlap between token chunks (fallback only)
|
|
49
|
+
|
|
50
|
+
# Documents and ask() (ADR-009)
|
|
51
|
+
document_store: Literal["local"] = "local" # where `document` bodies live
|
|
52
|
+
document_summary_chars: int = 1000 # characters of the body kept in the document's summary record
|
|
53
|
+
reasoner: Literal["local", "rlmstudio"] = "local" # ReasonerPort behind ask(); falls back to local
|
|
54
|
+
ask_top_k: int = 5 # chunks the local reasoner returns per ask()
|
|
55
|
+
|
|
56
|
+
# Sources, revalidation and store health (0.4)
|
|
57
|
+
project_root: str = "." # relative `source` paths resolve under this directory; nothing outside it is read
|
|
58
|
+
health_duplicate_threshold: float = (
|
|
59
|
+
0.95 # cosine similarity in [0, 1] from which two long-term memories are near-duplicates
|
|
60
|
+
)
|
|
61
|
+
health_duplicate_limit: int = 500 # most recent long-term memories checked for near-duplicates
|
|
62
|
+
health_report_limit: int = 20 # items listed per health category
|
|
63
|
+
|
|
64
|
+
# Lifecycle / decay (see docs/architecture/memory-tiers.md)
|
|
65
|
+
episode_ttl_days: int = 30 # base TTL for short-term `episode` memories
|
|
66
|
+
observation_ttl_days: int = 7 # base TTL for short-term `observation` memories
|
|
67
|
+
task_ttl_hours: int = 24 # base TTL for working-memory `task` records
|
|
68
|
+
access_boost: float = 0.5 # each recall extends TTL by this fraction of the base TTL
|
|
69
|
+
recency_half_life_days: float = 7.0 # recency decay half-life for short-term memories
|
|
70
|
+
|
|
71
|
+
# Recall scoring: final = w_relevance * fused + w_recency * recency + w_importance * importance
|
|
72
|
+
recall_weight_relevance: float = 1.0
|
|
73
|
+
recall_weight_recency: float = 0.3
|
|
74
|
+
recall_weight_importance: float = 0.3
|
|
75
|
+
|
|
76
|
+
# LLM wiki bridge: directory holding the markdown wiki (index.md, decisions/, ...).
|
|
77
|
+
# Empty = no wiki configured; wiki_export/wiki_import then require an explicit path.
|
|
78
|
+
wiki_dir: str = ""
|
|
79
|
+
|
|
80
|
+
# Server
|
|
81
|
+
transport: Literal["stdio", "streamable-http"] = "stdio"
|
|
82
|
+
host: str = "127.0.0.1"
|
|
83
|
+
port: int = 8765
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def settings_customise_sources(
|
|
87
|
+
cls,
|
|
88
|
+
settings_cls: type[BaseSettings],
|
|
89
|
+
init_settings: PydanticBaseSettingsSource,
|
|
90
|
+
env_settings: PydanticBaseSettingsSource,
|
|
91
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
|
92
|
+
file_secret_settings: PydanticBaseSettingsSource,
|
|
93
|
+
) -> tuple[PydanticBaseSettingsSource, ...]:
|
|
94
|
+
"""Default source order with the .env source limited to known settings.
|
|
95
|
+
|
|
96
|
+
Warns about MEMKERNEL_ keys in the environment or in .env that match no setting.
|
|
97
|
+
"""
|
|
98
|
+
for source, origin in ((env_settings, "the environment"), (dotenv_settings, ENV_FILE)):
|
|
99
|
+
for key in unknown_prefixed_keys(settings_cls, source):
|
|
100
|
+
logger.warning(
|
|
101
|
+
"memkernel: unknown setting %s in %s is ignored; check the spelling against the README", key, origin
|
|
102
|
+
)
|
|
103
|
+
return init_settings, env_settings, KnownFieldsOnly(dotenv_settings), file_secret_settings
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class KnownFieldsOnly(PydanticBaseSettingsSource):
|
|
107
|
+
"""Pass through only the values of ``inner`` that belong to a settings field."""
|
|
108
|
+
|
|
109
|
+
def __init__(self, inner: PydanticBaseSettingsSource) -> None:
|
|
110
|
+
super().__init__(inner.settings_cls)
|
|
111
|
+
self._inner = inner
|
|
112
|
+
|
|
113
|
+
def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
|
|
114
|
+
return self._inner.get_field_value(field, field_name)
|
|
115
|
+
|
|
116
|
+
def __call__(self) -> dict[str, Any]:
|
|
117
|
+
return {key: value for key, value in self._inner().items() if key in self.settings_cls.model_fields}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def unknown_prefixed_keys(settings_cls: type[BaseSettings], source: PydanticBaseSettingsSource) -> list[str]:
|
|
121
|
+
"""Return the MEMKERNEL_ keys ``source`` holds that are not fields of ``settings_cls``, upper-cased."""
|
|
122
|
+
if not isinstance(source, EnvSettingsSource):
|
|
123
|
+
return []
|
|
124
|
+
prefix = ENV_PREFIX.lower() # case-insensitive sources expose lower-cased keys
|
|
125
|
+
return sorted(
|
|
126
|
+
key.upper()
|
|
127
|
+
for key in source.env_vars
|
|
128
|
+
if key.lower().startswith(prefix) and key.lower()[len(prefix) :] not in settings_cls.model_fields
|
|
129
|
+
)
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 EGOGE
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
"""MemKernel configuration via environment variables."""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from typing import Literal
|
|
8
|
-
|
|
9
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class MemKernelSettings(BaseSettings):
|
|
13
|
-
"""All settings are configurable via MEMKERNEL_ prefixed env vars."""
|
|
14
|
-
|
|
15
|
-
model_config = SettingsConfigDict(env_prefix="MEMKERNEL_", env_file=".env")
|
|
16
|
-
|
|
17
|
-
project_id: str = "default"
|
|
18
|
-
default_session_id: str = "default"
|
|
19
|
-
|
|
20
|
-
# ChromaDB
|
|
21
|
-
chroma_persist_path: str = ".memkernel/chroma"
|
|
22
|
-
chroma_collection_prefix: str = "memkernel"
|
|
23
|
-
|
|
24
|
-
# Embedding
|
|
25
|
-
embedding_model: str = "all-MiniLM-L6-v2"
|
|
26
|
-
embedding_dimension: int = 384
|
|
27
|
-
|
|
28
|
-
# Retrieval
|
|
29
|
-
search_top_k: int = 5
|
|
30
|
-
rrf_k: int = 60
|
|
31
|
-
preview_length: int = 200
|
|
32
|
-
|
|
33
|
-
# Chunking
|
|
34
|
-
chunk_size: int = 1500 # max chars per chunk
|
|
35
|
-
chunk_overlap: int = 200 # char overlap between token chunks (fallback only)
|
|
36
|
-
|
|
37
|
-
# Documents and ask() (ADR-009)
|
|
38
|
-
document_store: Literal["local"] = "local" # where `document` bodies live
|
|
39
|
-
document_summary_chars: int = 1000 # characters of the body kept in the document's summary record
|
|
40
|
-
reasoner: Literal["local", "rlmstudio"] = "local" # ReasonerPort behind ask(); falls back to local
|
|
41
|
-
ask_top_k: int = 5 # chunks the local reasoner returns per ask()
|
|
42
|
-
|
|
43
|
-
# Sources, revalidation and store health (0.4)
|
|
44
|
-
project_root: str = "." # relative `source` paths resolve under this directory; nothing outside it is read
|
|
45
|
-
health_duplicate_threshold: float = (
|
|
46
|
-
0.95 # cosine similarity in [0, 1] from which two long-term memories are near-duplicates
|
|
47
|
-
)
|
|
48
|
-
health_duplicate_limit: int = 500 # most recent long-term memories checked for near-duplicates
|
|
49
|
-
health_report_limit: int = 20 # items listed per health category
|
|
50
|
-
|
|
51
|
-
# Lifecycle / decay (see docs/architecture/memory-tiers.md)
|
|
52
|
-
episode_ttl_days: int = 30 # base TTL for short-term `episode` memories
|
|
53
|
-
observation_ttl_days: int = 7 # base TTL for short-term `observation` memories
|
|
54
|
-
task_ttl_hours: int = 24 # base TTL for working-memory `task` records
|
|
55
|
-
access_boost: float = 0.5 # each recall extends TTL by this fraction of the base TTL
|
|
56
|
-
recency_half_life_days: float = 7.0 # recency decay half-life for short-term memories
|
|
57
|
-
|
|
58
|
-
# Recall scoring: final = w_relevance * fused + w_recency * recency + w_importance * importance
|
|
59
|
-
recall_weight_relevance: float = 1.0
|
|
60
|
-
recall_weight_recency: float = 0.3
|
|
61
|
-
recall_weight_importance: float = 0.3
|
|
62
|
-
|
|
63
|
-
# LLM wiki bridge: directory holding the markdown wiki (index.md, decisions/, ...).
|
|
64
|
-
# Empty = no wiki configured; wiki_export/wiki_import then require an explicit path.
|
|
65
|
-
wiki_dir: str = ""
|
|
66
|
-
|
|
67
|
-
# Server
|
|
68
|
-
transport: Literal["stdio", "streamable-http"] = "stdio"
|
|
69
|
-
host: str = "127.0.0.1"
|
|
70
|
-
port: int = 8765
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|