Metadata-Version: 2.4
Name: synaptic-core
Version: 0.4.0
Summary: Synaptic Core foundation and persistence layer for graph-backed memory.
Author-email: Xavier Hillman <xhillman13@gmail.com>
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic<2.12.0,>=2.7.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Provides-Extra: real-embedding
Requires-Dist: sentence-transformers<4.0.0,>=3.0.0; extra == "real-embedding"
Provides-Extra: proof-magic
Requires-Dist: faiss-cpu>=1.13.0; extra == "proof-magic"
Requires-Dist: chromadb>=1.5.0; extra == "proof-magic"
Requires-Dist: rank-bm25>=0.2.2; extra == "proof-magic"
Requires-Dist: scikit-learn>=1.4.0; extra == "proof-magic"

# synaptic-core

`synaptic-core` v0.4.0 ships a clean canonical API for graph memory:
- Client: `Synaptic` (async)
- Core methods: `remember`, `recall`, `set`, `get`, `find`, `delete`, `clear`, `connect`, `related`
- Session persistence: `store_session`, `retrieve_session`, `update_session`
- Session-first helpers: `client.session("...")`
- Structured errors: `SynapticError { code, message, hint }`

## Install

```bash
pip install synaptic-core
```

## Happy Path (Async, Session-First)

```python
import asyncio

from synaptic_core import Synaptic, SynapticError
from synaptic_core.types import OutcomeSignalType


def embed(text: str) -> list[float]:
    normalized = text.lower().strip()
    return [
        float(len(normalized.split())),
        float(len(normalized)) / 100.0,
        float(sum(ord(ch) for ch in normalized) % 257) / 257.0,
    ]


async def main() -> None:
    client = Synaptic(db_path="synaptic.db", embedding_fn=embed)
    session = client.session("chat-42")

    await session.remember("Customer asked for monthly usage CSV export")
    recall = await session.recall("How do I export monthly usage as CSV?", top_k=5)

    if recall.nodes:
        await session.feedback(
            recall.query_id,
            outcome=OutcomeSignalType.EXPLICIT_POSITIVE,
            provider="app",
        )

    print("nodes", [str(node.id) for node in recall.nodes])
    print("stats", await client.stats())


try:
    asyncio.run(main())
except SynapticError as exc:
    print(exc.as_dict())
```

## Constructor Runtime Options Contract

`Synaptic(...)` now validates runtime options before engine initialization:

- Unknown option names raise `SynapticError` with code `INVALID_CONSTRUCTOR_OPTIONS`.
- Invalid option values raise `SynapticError` with code `INVALID_CONSTRUCTOR_OPTIONS`.
- Conflicting alias/canonical pairs raise `SynapticError` with code `INVALID_CONSTRUCTOR_OPTIONS` (pass only one name per canonical option).
- Legacy aliases are accepted:
  - `retrieval_top_k` -> `top_k_retrieval`
  - `activation_seed_top_k` -> `top_k_activation_seeds`
  - `weekly_digest_ttl_seconds` -> `weekly_digest_cache_ttl_seconds`
  - `graph_confidence_refresh_interval` -> `graph_confidence_refresh_query_interval`

Supported runtime options:

- `backend`
- `classifier`
- `cross_tier_similarity_threshold`
- `cross_tier_initial_weight`
- `cross_tier_seed_top_k`
- `cross_tier_seed_max_edges`
- `top_k_retrieval`
- `top_k_activation_seeds`
- `telemetry_collector`
- `activation_engine`
- `outcome_detector`
- `connection_learner`
- `deployment_id`
- `weekly_digest_cache_ttl_seconds`
- `graph_confidence_refresh_query_interval`
- `graduation_engine`
- `graduation_scheduler_enabled`
- `graduation_check_interval_queries`
- `graduation_check_interval_seconds`
- `graduation_min_access_count`
- `graduation_min_positive_outcome_ratio`
- `graduation_min_age_hours`
- `graduation_min_reinforced_edges`
- `graduation_reinforced_edge_weight_threshold`
- `graduation_edge_transfer_threshold`
- `instance_optimizer`
- `instance_optimizer_enabled`
- `instance_optimizer_step_size`
- `instance_optimizer_min_sample_count`
- `instance_optimizer_update_interval`
- `session_deserializer`

Practical invalid-option example:

```python
from synaptic_core import Synaptic, SynapticError

try:
    Synaptic(db_path="synaptic.db", unknown_runtime_option=True)
except SynapticError as exc:
    assert exc.code == "INVALID_CONSTRUCTOR_OPTIONS"
```

## Canonical API

- `remember(content, *, session_id="default", memory_type=None, external_id=None, source_type=None, metadata=None) -> Node`
- `recall(query, *, session_id="default", top_k=None) -> RetrievalResult`
- `feedback(query_id, outcome, *, session_id=None, agent_response=None, user_next_message=None, dwell_time_ms=None, active_nodes=None, corrected_nodes=None, provider="unknown") -> CompositeOutcome`
- `set(key, value, *, namespace=None, metadata=None, ttl=None) -> None`
- `get(key, *, namespace=None) -> Any | None`
- `delete(key, *, namespace=None) -> bool`
- `clear(*, namespace=None) -> int`
- `store_session(session) -> Any`
- `retrieve_session(session_id) -> Any | None`
- `update_session(session) -> Any`
- `find(query, *, namespace=None, limit=10, filters=None) -> list[dict]`
- `connect(node_a_id, node_b_id, *, weight=0.3, connection_type="excitatory", formation_trigger="explicit", is_cross_tier=None) -> dict`
- `related(node_id, *, limit=None) -> list[dict]`
- `stats() -> dict`
- `graph_status() -> dict`
- `weekly_digest(force_refresh=False) -> dict`
- `session_summary(session_id) -> dict`
- `sessions_recent(limit=20) -> list[dict]`
- `queries_recent(session_id=None, limit=20) -> list[dict]`
- `outcomes_recent(session_id=None, limit=20) -> list[dict]`

## Session-First Helpers

- `client.session(session_id).remember(...)`
- `client.session(session_id).recall(...)`
- `client.session(session_id).feedback(...)`
- `client.session(session_id).summary()`
- `client.session(session_id).queries_recent(limit=20)`
- `client.session(session_id).outcomes_recent(limit=20)`

## First-Party Axis Interop

`axis-core` consumes the public `synaptic_core.Synaptic` client only. The maintained interop
surface for `axis_core.adapters.memory.synaptic.SynapticMemory` is:

- `Synaptic(...)`
- `client.session("...")`
- `set`, `get`, `find`, `delete`, `clear`
- `store_session`, `retrieve_session`, `update_session`

Axis declares support for `synaptic-core>=0.3.0,<0.4.0` through the optional
`axis-core[synaptic]` extra.

Source-tree validation against sibling checkouts:

```bash
# from axis-core/
PYTHONPATH="$(cd ../synaptic-core/src && pwd)" \
./scripts/test.sh tests/adapters/memory/test_synaptic.py tests/adapters/memory/test_synaptic_integration.py

# from synaptic-core/
.venv/bin/python -m pytest tests/synaptic_core/test_api_v03.py tests/synaptic_core/test_core_integration.py -q
```

The Axis command intentionally imports from `../synaptic-core/src` so the first-party contract is
validated against the sibling source tree instead of an installed wheel.

## CLI

```bash
synaptic doctor --db-path synaptic.db
synaptic stats --db-path synaptic.db --json
synaptic graph status --db-path synaptic.db --json
synaptic sessions recent --db-path synaptic.db --limit 20 --json
synaptic queries recent --db-path synaptic.db --session-id chat-42 --limit 20 --json
synaptic outcomes recent --db-path synaptic.db --session-id chat-42 --limit 20 --json
synaptic weekly-digest --db-path synaptic.db --json
```

## Test Marker Presets

Use marker-driven subsets for faster local validation loops:

```bash
.venv/bin/python -m pytest tests/synaptic_core -m public_api -q
.venv/bin/python -m pytest tests/synaptic_core -m service_unit -q
.venv/bin/python -m pytest tests/synaptic_core -m engine_contract -q
.venv/bin/python -m pytest tests/synaptic_core -m integration -q
.venv/bin/python -m pytest tests/synaptic_core -m durability -q
```
