Metadata-Version: 2.5
Name: cogkura
Version: 0.15.3
Summary: Research-driven cognitive memory framework for AI systems.
Project-URL: Homepage, https://cogkura.com
Project-URL: Repository, https://github.com/cogkura/cogkura
Project-URL: Issues, https://github.com/cogkura/cogkura/issues
Project-URL: Documentation, https://cogkura.com
Author-email: George Paterson <paterson.george@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: ai,cognitive,llm,memory,research
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.31.0; extra == 'postgres'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'postgres'
Description-Content-Type: text/markdown

# Cogkura

Research-driven cognitive memory framework for AI systems.

## Why Cogkura exists

Most AI applications keep useful data, but retrieval is often shallow. You either do direct lookup, keyword search, or vector similarity, and then pass results to an LLM with little memory structure.

Cogkura explores how research-backed cognitive memory mechanisms can improve how AI systems encode, consolidate, associate, and recall information.

## What Cogkura is not

Cogkura is not:

- a vector database;
- a RAG framework;
- an LLM provider;
- a hosted memory API;
- tied to one model, database, or agent framework.

## How Cogkura differs

- Storage systems optimize persistence and querying.
- Vector search optimizes similarity matching.
- RAG frameworks optimize context assembly for prompts.

Cogkura focuses on cognitive memory algorithms that sit between your data and your AI system.

You bring your own storage, ingestion, embeddings, and LLM provider. Cogkura supplies memory behavior and orchestration.

Cogkura owns observations and derived memories, not customer application records. Source connectors read customer data; Cogkura writes only to Cogkura-owned storage.

## Architecture

Cogkura is a memory layer, not a database or an agent runtime. Your schemas stay yours. Cogkura stores observations and the memories derived from them, then ranks those memories for the model you already use.

```mermaid
flowchart LR
  subgraph yours [Your stack]
    Src[Customer schemas]
    Agent[LLM / agent]
  end

  subgraph ck [Cogkura]
    Facade["Memory facade"]
    Owned["Observations, episodes, semantics, activation"]
  end

  Src -->|"observe / ingest, read-only"| Facade
  Facade --> Owned
  Owned -->|"recall, prepare_context"| Agent
  Agent -->|"record_context_use / learn"| Facade
```

Encoding is explicit. Stored observations are not recall candidates until you call `encode_episodes()` or `process()`, and facts are not slot memories until semantic consolidation runs. `prepare_context()` is presentation; `record_context_use()` is use.

```mermaid
flowchart TD
  subgraph appPath [Application path]
    observe["observe / ingest"] --> process["process"]
    process --> prepare["prepare_context"]
    prepare --> ctx[MemoryContext]
    ctx --> agent[External LLM / agent]
    agent --> record["record_context_use"]
    outcome[Outcome feedback] --> learn["learn"]
  end

  subgraph maintenancePath [Maintenance]
    maintenance[Scheduled maintenance] --> forget["apply_forgetting"]
  end

  observations[(Observations)] --> process
  process --> episodes[(Episodes)]
  process --> semantics[(Semantic memories)]
  episodes --> prepare
  semantics --> prepare
```

Lower-level APIs (`encode_episodes()`, `consolidate_semantics()`, `recall()`, `inspect_recall()`, `select_working_memory()`, `assess_memory()`, `record_access()`) remain available for research and advanced integrations. See [`docs/application-integration.md`](docs/application-integration.md).

See [`docs/architecture.md`](docs/architecture.md) for storage protocols, deployment models, and package layout.

## Installation

```bash
pip install cogkura
```

PostgreSQL support:

```bash
pip install "cogkura[postgres]"
```

## Quick start

```python
import asyncio
from datetime import UTC, datetime

from cogkura import Memory, ObservationInput


async def main() -> None:
    memory = Memory()
    tenant_id = "local"

    await memory.observe(
        ObservationInput(
            tenant_id=tenant_id,
            subject_id="george",
            source_namespace="direct",
            source_record_id="1",
            content="George discussed cognitive memory algorithms",
            observed_at=datetime.now(UTC),
            metadata={"conversation_id": "research", "source": "conversation"},
        )
    )

    await memory.encode_episodes(tenant_id=tenant_id)

    results = await memory.recall(
        "What was discussed about cognitive memory?",
        tenant_id=tenant_id,
    )

    for result in results:
        print(result.score, result.memory.statement, result.reason)

    memory.sleep()


asyncio.run(main())
```

## Application integration

For routine application use, orchestrate memory formation and context preparation without sequencing every cognitive primitive yourself. Cogkura prepares memory context; it does not call the LLM.

```python
import asyncio
from datetime import UTC, datetime

from cogkura import Memory, ObservationInput


async def main() -> None:
    memory = Memory()
    tenant_id = "shop"
    subject_id = "customer_42"

    await memory.observe(
        ObservationInput(
            tenant_id=tenant_id,
            subject_id=subject_id,
            source_namespace="orders",
            source_record_id="order_123",
            event_type="purchase",
            content="Customer purchased Nike Pegasus 41 in UK size 11.",
            observed_at=datetime.now(UTC),
            metadata={
                "semantic_facts": [
                    {
                        "predicate": "shoe_size",
                        "object_value": "UK 11",
                        "cardinality": "one",
                        "polarity": "affirm",
                    }
                ],
            },
        )
    )

    await memory.process(tenant_id=tenant_id, subject_id=subject_id)

    context = await memory.prepare_context(
        "I'd like another pair, but something lighter.",
        tenant_id=tenant_id,
        subject_id=subject_id,
        goal="Help the customer choose suitable running shoes.",
        prompt_budget_tokens=1500,
    )

    print(context.render())
    print("Estimated tokens:", context.estimated_tokens)
    print("Assessment flags:", list(context.assessment.flags))

    # Application-owned model call uses context.render() or structured fields.

    await memory.record_context_use(context)


asyncio.run(main())
```

See [`docs/application-integration.md`](docs/application-integration.md) for the full integration contract.

## Episodic memory encoding

After observations are stored, encode them into context-bound episodes:

```python
from datetime import UTC, datetime

from cogkura import Memory, ObservationInput

memory = Memory()

await memory.observe(
    ObservationInput(
        tenant_id="company_123",
        subject_id="customer_42",
        source_namespace="direct",
        source_record_id="message_1",
        content="Redis would add too much operational complexity.",
        observed_at=datetime.now(UTC),
        metadata={"conversation_id": "architecture_123"},
    )
)

result = await memory.encode_episodes(tenant_id="company_123", subject_id="customer_42")
episodes = await memory.list_episodes(tenant_id="company_123", subject_id="customer_42")

print(result.created, len(episodes[0].evidence))
```

Pass `as_of=` when replaying a frozen timeline; omit it for live encoding.

## Semantic consolidation

Attach structured facts to observation metadata, encode episodes, then consolidate:

```python
semantic_fact = {
    "predicate": "preferred_database",
    "object_value": "postgresql",
    "object_entity_id": "postgresql",
    "cardinality": "one",
    "polarity": "affirm",
    "qualifiers": {"environment": "production"},
}

await memory.observe(
    ObservationInput(
        tenant_id="company_123",
        subject_id="customer_42",
        source_namespace="direct",
        source_record_id="message_1",
        content="PostgreSQL fits our operational constraints.",
        observed_at=datetime.now(UTC),
        metadata={
            "conversation_id": "architecture_123",
            "semantic_facts": [semantic_fact],
        },
    )
)

await memory.encode_episodes(tenant_id="company_123", subject_id="customer_42")
result = await memory.consolidate_semantics(tenant_id="company_123", subject_id="customer_42")
memories = await memory.list_semantic_memories(tenant_id="company_123", subject_id="customer_42")

print(result.promoted, memories[0].statement)
```

Pass the same `as_of=` used for encoding when consolidating a simulated timeline.

## Declarative activation (recall)

After encoding (and optionally consolidating), recall ranks episodic and semantic memories with ACT-R base-level accessibility, spreading activation, query-coverage partial matching, soft semantic slot admission, temporal/current-state policy, and global candidate ordering. Precision-aware text matching refines order among eligible candidates; it does not control the retrieval threshold. String queries seed spreading sources from cue tokens that overlap candidate entity ids; explicit `RetrievalCue.entity_ids` can soft-admit matching slot semantics and SUPPORT episodes without rank priority. Near-duplicate statements are collapsed before the rank limit is applied. Current-state bonuses apply to cue-matched semantic slots; superseded-only SUPPORT is excluded on live current-state retrieval. Historical `valid_at` admission uses the visible revision rather than present-day ACTIVE status.

`recall()` is presentation. `record_access()` records use.

```mermaid
flowchart TD
  cue[Retrieval cue] --> load[Load episodes and semantics]
  load --> filter["Filter forgotten and valid_at"]
  filter --> activate[Base-level, spreading, partial match]
  activate --> admit[Eligibility and slot admission]
  admit --> rank[Global ranking]
  rank --> collapse[Near-duplicate collapse]
  collapse --> results[RecallResult list]
  results -.-> access["record_access is a separate call"]
```

```python
from datetime import UTC, datetime

from cogkura import ActivationConfig, RetrievalCue

results = await memory.recall(
    RetrievalCue(text="preferred database for production", subject_id="customer_42"),
    tenant_id="company_123",
)

# String cues can seed spreading from candidate entity overlap.
# Explicit entity_ids keep 0.11 associative behaviour.
results = await memory.recall(
    RetrievalCue(
        text="What database was involved?",
        entity_ids=("alice",),
    ),
    tenant_id="company_123",
)

# Historical recall: semantics use revision windows; episodes need started_at <= valid_at
as_of = datetime(2026, 1, 6, tzinfo=UTC)
results = await memory.recall(
    "What did we currently use for job coordination?",
    tenant_id="company_123",
    as_of=as_of,
    valid_at=as_of,
)

for result in results:
    print(result.activation, result.score, result.memory.statement)

# record_access is use, not presentation — filter weak rows when needed
await memory.record_access(results, tenant_id="company_123", min_score=0.5)

# Forgetting maintenance (explicit; sleep() is a no-op)
result = await memory.apply_forgetting(tenant_id="company_123", as_of=as_of)
```

For simulated replay, pass the same `as_of` to `encode_episodes()` and `consolidate_semantics()` so `created_at` is not wall clock. Live callers can omit it.

Tune retrieval with `activation_config=ActivationConfig(retrieval_threshold=-1.0)` on `Memory(...)`. See [`docs/design-ranking-time-current-state.md`](docs/design-ranking-time-current-state.md) and [`docs/design-string-cues-current-state.md`](docs/design-string-cues-current-state.md).

For PostgreSQL, pass `PostgresObservationStore`, `PostgresEpisodeStore`, `PostgresSemanticMemoryStore`, `PostgresActivationStore`, `PostgresMemoryDynamicsStore`, and `PostgresLearningStore` to `Memory`.

See [`docs/forgetting.md`](docs/forgetting.md) for lifecycle thresholds and compaction details.

## Metamemory (memory assessment)

`assess_memory()` reports the state of currently retrievable memory. It does not record access, apply forgetting, or create learning feedback, and it does not produce an overall confidence score.

```python
assessment = await memory.assess_memory(
    "What database did we select for production?",
    tenant_id="company_123",
    goal="Recall the production database decision.",
)

print(assessment.signals.cue_coverage)
print(assessment.signals.top_retrieval_strength)
print(assessment.signals.evidence_confidence)
print(assessment.signals.semantic_conflict)
print(assessment.flags)
```

See [`docs/metamemory.md`](docs/metamemory.md) and [`examples/metamemory.py`](examples/metamemory.py).

## Observation ingestion (PostgreSQL)

```python
from sqlalchemy.ext.asyncio import create_async_engine

from cogkura import Memory
from cogkura.sources.postgres import PostgresTableSource
from cogkura.storage.postgres import (
    PostgresActivationStore,
    PostgresCheckpointStore,
    PostgresEpisodeStore,
    PostgresLearningStore,
    PostgresMemoryDynamicsStore,
    PostgresObservationStore,
    PostgresSemanticMemoryStore,
)

memory_engine = create_async_engine("postgresql+asyncpg://...")
source_engine = create_async_engine("postgresql+asyncpg://...")

memory = Memory(
    observation_store=PostgresObservationStore(memory_engine),
    checkpoint_store=PostgresCheckpointStore(memory_engine),
    episode_store=PostgresEpisodeStore(memory_engine),
    semantic_store=PostgresSemanticMemoryStore(memory_engine),
    activation_store=PostgresActivationStore(memory_engine),
    dynamics_store=PostgresMemoryDynamicsStore(memory_engine),
    learning_store=PostgresLearningStore(memory_engine),
)

source = PostgresTableSource(
    connector_id="application-messages",
    engine=source_engine,
    table="public.messages",
    cursor_columns=("updated_at", "id"),
)

result = await memory.ingest(
    source=source,
    mapper=MessageMapper("company_123"),
    tenant_id="company_123",
)
```

Direct observation:

```python
from datetime import UTC, datetime

from cogkura import ObservationInput

status = await memory.observe(
    ObservationInput(
        tenant_id="company_123",
        subject_id="user_456",
        source_namespace="chat.messages",
        source_record_id="message_789",
        source_version="1",
        event_type="message",
        content="I prefer PostgreSQL for production services.",
        observed_at=datetime.now(UTC),
    )
)
```

Checkpoints advance only after a successful batch. Cogkura does not write to customer source tables.

```mermaid
flowchart LR
  Tables[(Customer tables)] --> Connector[PostgresTableSource]
  Connector --> Mapper[ObservationMapper]
  Mapper --> Pipeline[Policy and retention]
  Pipeline --> ObsStore[(ObservationStore)]
  Connector -->|"cursor after successful batch"| Checkpoint[CheckpointStore]
```

See [`examples/postgres_datasource/README.md`](examples/postgres_datasource/README.md) for the full Docker-based demo.

### Postgres example environment

Unit tests and the basic in-memory example do not need Docker or env vars.

For the Postgres demo and `@pytest.mark.postgres` integration tests:

```bash
cd examples/postgres_datasource
docker compose up -d
cp .env.example .env
```

Example `.env` (also in [`.env.example`](examples/postgres_datasource/.env.example)):

```bash
# Read-only source DB (demo + most integration tests)
COGKURA_POSTGRES_SOURCE_URL=postgresql+asyncpg://cogkura_reader:cogkura_reader@localhost:5432/cogkura_source

# Cogkura write DB (demo + most integration tests)
COGKURA_POSTGRES_MEMORY_URL=postgresql+asyncpg://cogkura_writer:cogkura_writer@localhost:5432/cogkura_memory

# Optional: write access for mutate.py / admin test inserts
COGKURA_POSTGRES_SOURCE_ADMIN_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/cogkura_source

# Optional: owner role for schema migrations / upgrade tests
COGKURA_POSTGRES_MEMORY_ADMIN_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/cogkura_memory

# Optional: same-DB schema mode tests
COGKURA_POSTGRES_SAME_DB_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/cogkura_source
```

Load the file into your shell before running the demo or Postgres tests:

```bash
set -a && source examples/postgres_datasource/.env && set +a
uv run python examples/postgres_datasource/demo.py
uv run pytest -m postgres
```

`mutate.py` needs write access to the source database. Prefer `COGKURA_POSTGRES_SOURCE_ADMIN_URL`, or run with the script default (`postgres` on `cogkura_source`), not the read-only `cogkura_reader` URL.

## Current status

Cogkura is in development. Through `0.15.3`, the library provides application integration via `Memory.process()`, `Memory.prepare_context()`, `MemoryContext`, and `Memory.record_context_use()`, plus observation ingestion, episodic encoding, semantic consolidation with temporal reconsolidation, ACT-R declarative activation with global eligible-candidate ranking, spreading activation, Ebbinghaus-inspired forgetting dynamics, bounded working-memory selection with precision-aware goal relevance, outcome-driven learning via `Memory.learn()`, and read-only metamemory assessment via `Memory.assess_memory()`, with explicit `record_access()` / `record_context_use()` reinforcement (presentation vs use), `apply_forgetting()` maintenance, simulated `as_of` on encode/consolidate/process, episode `valid_at` filtering, candidate-set IDF ranking, near-duplicate collapse, temporal current-state policy, soft entity slot admission, coverage-based accessibility with precision-aware ranking, conjunctive structured slot matching, positive bounded structured ranking, retrieval diagnostics with explicit eligibility and provenance, metamemory answerability, multi-entity conjunction, incident tag seeding, superseded-only SUPPORT exclusion, metamemory `MISSING_KNOWLEDGE`, working-memory same-slot collapse, evidence-chronology activation with `inspect_recall()`, lexical semantic slot matching for plain-language cues, bounded soft admission for long-horizon current facts, evidence-linked associative semantic reachability, and authoritative current semantic admission.

## Scope of 0.15.3

Implemented in `0.15.3`:

- cardinality-one reconciliation by supporting evidence chronology when validity windows are unspecified;
- bounded evidence-linked semantic relevance from supporting episode statements in the candidate set;
- authoritative current semantic admission (`SEMANTIC_CURRENT_ADMISSION`) with `semantic_current_min_relevance`;
- inspection diagnostics for direct vs evidence-linked cue fit;
- [`tests/test_semantic_state_associative_recall.py`](tests/test_semantic_state_associative_recall.py).

## Scope of 0.15.2

Implemented in `0.15.2`:

- lexical semantic slot matching for plain-language string cues;
- bounded semantic soft admission (`semantic_soft_admission_floor`, `max_soft_admitted_semantics`);
- inspection dispositions for below soft floor and insufficient lexical relevance;
- [`tests/test_long_horizon_semantic_recall.py`](tests/test_long_horizon_semantic_recall.py).

## Scope of 0.15.1

Implemented in `0.15.1`:

- derived cognitive activation references from episode and semantic evidence chronology (no migration);
- processing-cadence recall stability for equivalent source evidence;
- `Memory.inspect_recall()` with terminal dispositions and activation diagnostics;
- [`docs/declarative-activation.md`](docs/declarative-activation.md) cognitive chronology and inspection notes.

## Scope of 0.15.0

Implemented in `0.15.0`:

- `Memory.process()` orchestrates episodic encoding and semantic consolidation with one evaluation timestamp;
- `Memory.prepare_context()` returns bounded working memory and metamemory assessment in one read operation;
- `MemoryContext` structured boundary with deterministic `render()`;
- `Memory.record_context_use()` records use of selected context memories;
- shared declarative retrieval inside `prepare_context()` (one rank pass per call);
- [`docs/application-integration.md`](docs/application-integration.md) and [`examples/application_context.py`](examples/application_context.py).
- [`docs/design-application-integration-memory-context-0.15.0.md`](docs/design-application-integration-memory-context-0.15.0.md).

Not implemented in `0.15.0`:

- full REDACTED / REFERENCE_ONLY retention modes;
- non-PostgreSQL source connectors.

## Long-term cognitive architecture

Target conceptual flow:

```mermaid
flowchart TD
  data[Data and experiences] --> encode[Event encoding]
  encode --> episodic[Episodic memory]
  episodic --> semantic[Semantic consolidation]
  semantic --> world[Associative world model]
  world --> spreading[Spreading activation]
  spreading --> goal[Goal relevance and inhibition]
  goal --> wm[Bounded working memory]
  wm --> assess[Memory assessment]
  assess --> llm[LLM reasoning and planning]
  llm --> outcome[Outcome feedback]
  outcome --> learn[Learning / reinforcement]
```

## Roadmap

- `0.1`: PostgreSQL observation ingestion and provenance.
- `0.2`: episodic memory encoding, salience, temporal context, and evidence links (done).
- `0.3`: semantic consolidation from episodic memories (done).
- `0.4`: declarative activation (ACT-R recall over episodic + semantic memories) (done).
- `0.5`: spreading activation (done).
- `0.6`: forgetting / memory dynamics (done).
- `0.7`: working-memory selection and inhibition (done).
- `0.8`: temporal reconsolidation and memory updating (done).
- `0.9`: learning and reinforcement (done).
- `0.10`: metamemory / memory monitoring (done).
- `0.11`: ranking, simulated time, and current-state recall (done).
- `0.12`: string cues, slot admission, and access recording (done).
- `0.13`: gated slot admission, association, and metamemory (done).
- `0.14`: retrieval eligibility, global ranking, temporal relevance, and cue discrimination (done).
- `0.14.1`: retrieval corrections and ranking separation (done).
- `0.14.2`: temporal retrieval mode, structured slot fit, and metamemory answerability (done).
- `0.14.3`: conjunctive slot matching and positive structured ranking (done).
- `0.14.4`: retrieval diagnostics and SUPPORT provenance (done).
- `0.15.0`: application integration and memory context (done).
- `0.15.1`: recall stability from evidence chronology and `inspect_recall()` (done).
- `0.15.2`: long-horizon semantic recall via lexical slot matching and bounded soft admission (done).
- `0.15.3`: semantic state reconciliation, evidence-linked associative recall, and authoritative current admission (done).
- later: additional connectors, and integrations.

See [`docs/roadmap.md`](docs/roadmap.md) and [`docs/architecture.md`](docs/architecture.md) for details.

## Development setup with uv

```bash
uv sync --all-extras --dev
```

## Validation commands

```bash
uv run ruff check .
uv run ruff format .
uv run mypy src
uv run pytest
```

## Build commands

```bash
uv build
uvx twine check dist/*
```

## Contributing

Contributions are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md), then open an issue or pull request.

Agent and editor guidance lives in [`AGENTS.md`](AGENTS.md) (primary). [`CLAUDE.md`](CLAUDE.md) points there.

## License

Licensed under the Apache License, Version 2.0. See [`LICENSE`](LICENSE).
