Skip to content

feat: add dakera-haystack integration — self-hosted memory & document store for Haystack - #3515

Closed
ferhimedamine wants to merge 9 commits into
deepset-ai:mainfrom
ferhimedamine:feat/dakera-integration
Closed

ferhimedamine wants to merge 9 commits into
deepset-ai:mainfrom
ferhimedamine:feat/dakera-integration

Conversation

@ferhimedamine

@ferhimedamine ferhimedamine commented Jul 1, 2026 •

Copy link
Copy Markdown

Closes #3516
Supersedes #3519 (the document-store integration is now included here; #3519 is closed as redundant)

Summary

Adds integrations/dakera/ — a single dakera-haystack package for Dakera, a self-hosted memory server. It exposes two complementary integrations that share one Dakera server:

  • Memory — DakeraMemoryStore, DakeraMemoryRetriever, DakeraMemoryWriter: conversational, decay-weighted memory over Dakera's memory API (POST /v1/memory/store, POST /v1/memory/recall). ChatMessage-based; the server handles embedding and importance scoring.
  • Document store — DakeraDocumentStore, DakeraEmbeddingRetriever: a standard Haystack DocumentStore over Dakera's vector-namespace API (via the official dakera Python SDK), for embedding-based document retrieval (RAG) with metadata filtering.

This follows the structure of integrations/mem0/ and (for the document store) the integrations/pinecone/ conventions.

Components

Class Kind Description
DakeraMemoryStore Client Memory API client; X-API-Key auth
DakeraMemoryWriter @component Persists ChatMessage text as memories → memories_written: int
DakeraMemoryRetriever @component Decay-weighted recall → memories: list[ChatMessage]
DakeraDocumentStore DocumentStore Vector-namespace document store (sync + async) backed by the dakera SDK
DakeraEmbeddingRetriever @component Dense document retrieval with metadata filtering

Tests

  • Unit tests use respx (memory) and AsyncMock/mocked SDK (document store) — no live server needed in CI.
  • Integration tests (test_memory_integration.py, test_document_integration.py) run against a live Dakera server and are skipped unless DAKERA_API_URL / DAKERA_URL is set. The document-store suite reuses Haystack's CountDocumentsTest / DeleteDocumentsTest / WriteDocumentsTest mixins.
cd integrations/dakera
hatch run test:unit          # mocked, runs in CI
hatch run test:integration   # requires a live Dakera server

Notes

  • dependencies = haystack-ai>=2.28.0, httpx>=0.27.0, dakera>=0.12.8.
  • Self-host via dakera-deploy; the REST API listens on port 3000 by default.

New integration package: integrations/dakera/

Provides:
- DakeraMemoryStore: httpx-based client for Dakera REST API
  POST /v1/memories (store) + POST /v1/memories/search (recall)
- DakeraMemoryRetriever: @component wrapping store.search_memories()
- DakeraMemoryWriter: @component wrapping store.store_memories()
- Unit tests using respx HTTP mocking (no live server needed)

Follows the mem0-haystack package structure exactly.
Package name: dakera-haystack (pip install dakera-haystack)
Version tag: integrations/dakera-v*

Self-host: docker run -p 3000:3000 dakera/dakera:latest
@ferhimedamine
ferhimedamine requested a review from a team as a code owner July 1, 2026 06:31
@ferhimedamine
ferhimedamine requested review from julian-risch and removed request for a team July 1, 2026 06:31
@github-actions github-actions Bot added the type:documentation Improvements or additions to documentation label Jul 1, 2026
@ferhimedamine

Copy link
Copy Markdown
Author

Fixing the test-api-reference-build CI failure — the pyproject.toml referenced readme = "README.md" but the file was missing from the integration directory. Added integrations/dakera/README.md with full component documentation.

@CLAassistant

CLAassistant commented Jul 1, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@ferhimedamine

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

…build CI

The CI runs 'hatch run docs:build' which requires:
- haystack-pydoc-tools in [tool.hatch.envs.default] dependencies
- docs script in [tool.hatch.envs.default.scripts]
- pydoc/config_docusaurus.yml pointing to our public module paths

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@ferhimedamine

Copy link
Copy Markdown
Author

Fixed the test-api-reference-build CI failure — the hatch run docs command was failing with docs: not found because:

  1. haystack-pydoc-tools was missing from [tool.hatch.envs.default] dependencies
  2. The docs script was missing from [tool.hatch.envs.default.scripts]
  3. pydoc/config_docusaurus.yml did not exist

Added all three in the latest commit, following the pattern from integrations/anthropic/pyproject.toml.

@ferhimedamine

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@julian-risch julian-risch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ferhimedamine thank you for opening this PR and for your patience! There are a couple of smaller issues with the PR that block us from merging it and releasing the integration. Could you please update the PR and address the following issues please?

  • test_to_dict_round_trip will fail in CI because the store fixture uses Secret.from_token("test-key"). Should be Secret.from_env_var("DAKERA_API_KEY", strict=False) instead
  • The code examples in the README all have errors and can't be run. (import paths, ...)
  • Tests are not running because there is no workflow file

I recommend to have a look at integrations/mem0 as an example and potentially use our scaffolding script, which will generate the workflow file automatically for you and also the missing LICENSE.txt: https://github.com/deepset-ai/haystack-core-integrations/blob/main/CONTRIBUTING.md#create-a-new-integration

Addresses @julian-risch review on PR deepset-ai#3515:
- tests: use Secret.from_env_var(..., strict=False) so to_dict round-trips.
  Secret.from_token is not serializable and broke test_to_dict_round_trip.
- README: fix the broken import paths, `memory_store=` param and run() args in
  the usage example; correct the configuration table to the real constructor
  parameters; add a Contributing section.
- add .github/workflows/dakera.yml so tests actually run in CI (modeled on the
  mem0 integration workflow).
- add LICENSE.txt (Apache-2.0).
- pyproject: add the hatch `test` env plus ruff/mypy/coverage/pytest config that
  back the workflow scripts (fmt-check, test:types, test:unit-cov-retry).
- wire the integration into labeler.yml, CI_coverage_comment.yml and the root
  README table.

Also fixes two real bugs surfaced while making CI green:
- Haystack's structlog logger rejects %-style positional args, so every
  logger.* call now uses {}-placeholders with keyword args (previously raised
  TypeError at runtime in the writer/retriever and error paths).
- add py.typed markers so `hatch run test:types` (mypy) passes.
- guard base_url resolution against a None env value (mypy union-attr).

Verified locally: ruff check + ruff format --check clean, mypy clean,
11 unit tests pass at 99% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ferhimedamine

Copy link
Copy Markdown
Author

Thanks for the thorough review, @julian-risch — all three points are addressed, and I took the integrations/mem0 layout as the reference as you suggested.

1. test_to_dict_round_trip failing in CI
The store fixture now uses Secret.from_env_var("DAKERA_API_KEY", strict=False) instead of Secret.from_token(...), so to_dict()/from_dict() serialize cleanly. The fixture sets the env var via monkeypatch so the test is self-contained.

2. README examples
The usage example was broken — wrong import paths, a store= kwarg that doesn't exist, and run() arguments that didn't match the components. It's been rewritten and now reflects the real API:

  • from haystack_integrations.memory_stores.dakera import DakeraMemoryStore
  • from haystack_integrations.components.retrievers.dakera import DakeraMemoryRetriever
  • from haystack_integrations.components.writers.dakera import DakeraMemoryWriter
  • components take memory_store=...; writer.run(messages=...) / retriever.run(query=...)

The configuration table now lists the actual constructor parameters, and I added a Contributing section.

3. Tests not running (no workflow)
Added .github/workflows/dakera.yml (modeled on mem0.yml) plus the missing LICENSE.txt, and wired the integration into labeler.yml, CI_coverage_comment.yml, and the root README table. pyproject.toml gained the test hatch env and the ruff/mypy/coverage/pytest config that back fmt-check, test:types, and test:unit-cov-retry.

While making CI green I also caught and fixed two real bugs:

  • Haystack's structlog logger rejects %-style positional args, so every logger.* call now uses {}-placeholders with keyword args (previously raised TypeError at runtime in the writer/retriever paths).
  • Added py.typed markers so hatch run test:types (mypy) passes, and guarded base_url resolution against a None env value.

CI is now green across the full matrix (Python 3.10 & 3.14 on Linux, Windows, and macOS): https://github.com/deepset-ai/haystack-core-integrations/actions/runs/30078491113 — fmt-check, mypy, and 11 unit tests at 99% coverage.

Ready for another look whenever you have a moment. 🙏

@julian-risch
julian-risch self-requested a review July 24, 2026 14:54

@julian-risch julian-risch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ferhimedamine Thanks for addressing the review. Unfortunately, there are still a number of inconsistencies, for example:

  • PR body: docker run -p 3000:3000 vs. README & code default: port 3300
  • Untyped output socket. integrations/dakera/src/haystack_integrations/components/retrievers/dakera/retriever.py#L722 uses @component.output_types(memories=list). mem0's equivalent uses list[ChatMessage]. A bare list weakens pipeline connection type-checking.
  • integration tests should be added

@ferhimedamine

Copy link
Copy Markdown
Author

@ferhimedamine Thanks for addressing the review. Unfortunately, there are still a number of inconsistencies, for example:

  • PR body: docker run -p 3000:3000 vs. README & code default: port 3300
  • Untyped output socket. integrations/dakera/src/haystack_integrations/components/retrievers/dakera/retriever.py#L722 uses @component.output_types(memories=list). mem0's equivalent uses list[ChatMessage]. A bare list weakens pipeline connection type-checking.
  • integration tests should be added

Thx for feedback @julian-risch , i will adress all reported issues

…ped ChatMessage sockets

Addresses @julian-risch's second review (review-4774305947). A deep audit of the
Dakera server source (crates/api) showed the client targeted endpoints and a port
that do not exist, so this corrects the integration against the verified API:

- Port: default is 3000 (server `DAKERA_PORT`, Dockerfile `EXPOSE 3000`,
  dakera-deploy), not 3300 — fixed the code default, README and PR body so all three
  agree.
- Routes: create is `POST /v1/memory/store` and recall is `POST /v1/memory/recall`
  (the previous `/v1/memories` and `/v1/memories/search` do not exist and would 404).
- Auth: send `X-API-Key` (the server's default header; it also accepts Bearer).
- Schema: identity is `agent_id` (required) — dropped the non-existent `user_id`;
  recall results are parsed from `memories[]` where each item is `{memory: {...}, score}`.

Typed pipeline sockets (review point on the bare `list`):
- `DakeraMemoryWriter` now accepts `list[ChatMessage]` and stores each message's text.
- `DakeraMemoryRetriever` now declares `@component.output_types(memories=list[ChatMessage])`
  and returns recalled memories as system-role `ChatMessage` objects, with Dakera
  fields (id, score, importance, ...) under `meta` — mirroring `integrations/mem0`.

Integration tests (review point): added `tests/test_integration.py` (marked
`integration`, skipped unless `DAKERA_API_URL` is set) and re-enabled the integration
step in the workflow.

Verified locally: ruff check + format --check clean, mypy clean, docs build (pydoc)
clean, 13 unit tests pass at 93% coverage, integration tests skip without a live server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ferhimedamine

Copy link
Copy Markdown
Author

Thanks @julian-risch — all three points are addressed, and I did a deeper audit against the Dakera server itself rather than just patching the docs. That surfaced that the client was targeting endpoints/a port that don't actually exist, so I corrected the whole API surface:

1. Port inconsistency (3000 vs 3300)
The server's default REST port is 3000 (DAKERA_PORT, EXPOSE 3000 in the image, and the dakera-deploy compose all agree). 3300 was the bug — the code default, README, and PR body now all use 3000 consistently.

2. Untyped output socket
DakeraMemoryRetriever now declares @component.output_types(memories=list[ChatMessage]) and returns recalled memories as system-role ChatMessage objects (Dakera fields — id, score, importance, … — under each message's meta), mirroring integrations/mem0. DakeraMemoryWriter now accepts list[ChatMessage] as input, so both ends type-check cleanly in a pipeline.

3. Integration tests
Added tests/test_integration.py (marked integration, skipped unless DAKERA_API_URL points at a live server) covering a store→recall round trip and the component pair, and re-enabled the integration step in the workflow.

Additional correctness fixes from the audit (these were real bugs):

  • Endpoints: create is POST /v1/memory/store and recall is POST /v1/memory/recall. The previous /v1/memories and /v1/memories/search don't exist on the server and would have 404'd.
  • Auth: the client now sends X-API-Key (the server's default header; Bearer is also accepted).
  • Schema: the identity field is agent_id (required) — I removed the non-existent user_id; recall responses are parsed from memories[] where each item is {memory: {...}, score}.

CI is green across the full matrix (Python 3.10 & 3.14 on Linux, Windows, macOS), including the docs build and the new integration step: https://github.com/deepset-ai/haystack-core-integrations/actions/runs/30109488066 — ruff, mypy, 13 unit tests at 93% coverage.

Ready for another look. 🙏

…era-haystack package

Consolidates the two Dakera-Haystack PRs into a single package (per founder decision):
merges the document-store integration (deepset-ai#3519) into the memory integration (deepset-ai#3515), so
`dakera-haystack` now ships both, sharing one Dakera server.

Brought in from deepset-ai#3519:
- document_stores/dakera/{document_store.py, filters.py} — DakeraDocumentStore (sync + async)
  over the `dakera` SDK vector-namespace API, with metadata filtering.
- components/retrievers/dakera/embedding_retriever.py — DakeraEmbeddingRetriever.
- tests: test_document_store.py, test_document_store_async.py, test_embedding_retriever.py,
  test_filters.py, conftest.py, and the mixin-based document integration suite
  (renamed to test_document_integration.py). The memory integration suite is renamed to
  test_memory_integration.py.
- examples/, CHANGELOG.md, py.typed markers for the document-store packages.

Reconciled shared files:
- pyproject.toml: dependencies now haystack-ai>=2.28.0 + httpx>=0.27.0 + dakera>=0.12.8;
  test env gains pytest-xdist; mypy `types` covers all four packages; pytest asyncio auto mode.
- components/retrievers/dakera/__init__.py exports both DakeraEmbeddingRetriever and
  DakeraMemoryRetriever.
- README, pydoc config, and CHANGELOG document both the memory and document-store components.
- CI matrix pinned to Python 3.10 & 3.13.

Verified locally: ruff check + format --check clean, mypy clean over all 4 packages, docs
build clean, 74 unit tests pass at 91% coverage, integration tests skip without a live server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ferhimedamine ferhimedamine changed the title feat: add dakera-haystack integration — self-hosted persistent memory for Haystack pipelines feat: add dakera-haystack integration — self-hosted memory & document store for Haystack Jul 24, 2026
@socket-security

socket-security Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​dakera@​0.12.12100100100100100

View full report

@ferhimedamine

Copy link
Copy Markdown
Author

Heads-up on a consolidation, @julian-risch: we had two open PRs for Dakera — this one (#3515, the memory integration) and #3519 (the document-store integration). Since both were building the same dakera-haystack package in integrations/dakera/, only one can own that path — so I've merged the document-store work into this PR and am closing #3519 as redundant.

dakera-haystack now ships both, sharing one Dakera server:

  • Memory — DakeraMemoryStore / DakeraMemoryRetriever / DakeraMemoryWriter (decay-weighted ChatMessage memory).
  • Document store — DakeraDocumentStore (sync + async) + DakeraEmbeddingRetriever (dense retrieval with metadata filtering), via the official dakera Python SDK.

All of your review feedback from both PRs is incorporated:

CI is green across the matrix (Python 3.10 & 3.13 on Linux/Windows/macOS), including the docs build: https://github.com/deepset-ai/haystack-core-integrations/actions/runs/30113924150 — ruff, mypy over all packages, 74 unit tests at 91% coverage; integration tests are skipped in CI (no live server).

The updated PR description above reflects the unified package. Ready for another look — thanks for the thorough reviews on both PRs. 🙏

ferhimedamine and others added 2 commits July 24, 2026 18:04
…stent headers, version config

Proactive review-hardening pass on the consolidated dakera-haystack package:

- Async parity: DakeraMemoryStore now has store_memories_async / recall_memories_async, and
  DakeraMemoryWriter / DakeraMemoryRetriever implement run_async — matching the document store's
  async support so the whole package is uniformly sync + async. Sync/async share DRY payload
  builders. Added respx-based async unit tests.
- Consistency: unified the SPDX copyright headers across all files to Dakera AI (they were a mix
  of "deepset GmbH" and "Dakera AI" after the merge).
- Versioning: aligned the hatch-vcs config to the repo convention used by mem0/pinecone
  (`root = "../.."` + `git_describe_command` matching the integration's own tags) instead of the
  fallback-version style.

Verified locally: ruff check + format --check clean, mypy clean over all four packages, docs build
clean, a wheel builds with every module + all py.typed markers, the pinned floor `dakera==0.12.8`
exports every SDK symbol imported, and 80 unit tests pass at 92% coverage (integration tests skip
without a live server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds end-to-end coverage proving both integrations work inside a Haystack Pipeline:
`Pipeline.loads(Pipeline().dumps())` round-trips the components (env-var Secret
serialization), and the deserialized pipeline runs — memory (writer + retriever, HTTP
mocked with respx) and document store (embedding retriever, SDK mocked). Guards against
serialization/wiring regressions a maintainer would exercise.

Verified locally: ruff + format clean, 82 unit tests pass (integration tests skip
without a live server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@julian-risch

Copy link
Copy Markdown
Member

Hi @ferhimedamine and thanks again for opening this PR! I recommend to publish this integration yourselves under https://github.com/Dakera-AI similar to your other framework integration(s). We do have a repository template that you could use for that: https://github.com/deepset-ai/custom-component

To support you with the integration, I'd like to share a couple changes that are still required to make this new integration work well in your own repo:

  • Copyright headers assign the code to Dakera AI. That works in your repo so you can leave that as is there but not in haystack-core-integrations.
  • Four empty init.py files break haystack_integrations as a namespace package. Similarly, that will work differently in your own repo.
  • dakera[async] extra is not declared although async_client.py raises ImportError: httpx is required for AsyncDakeraClient. Install it with: pip install dakera[async].
  • Two different env vars for the same server URL: DAKERA_API_URL / DAKERA_URL
  • Integration tests
  • test_document_integration.py uses only CountDocumentsTest, DeleteDocumentsTest, WriteDocumentsTest. Missing from haystack.testing.document_store: FilterDocumentsTest, DocumentStoreBaseTests, DocumentStoreBaseExtendedTests, and the async part: haystack.testing.document_store_async provides CountDocumentsAsyncTest, WriteDocumentsAsyncTest, FilterDocumentsAsyncTest, DeleteDocumentsAsyncTest
  • LOGICAL_OPERATORS = {"AND": "$and", "OR": "$or"} is missing NOT but docstrings imply it's supported
  • Hand-written CHANGELOG.md: this file get generated. no need to add it manually.
  • The memory store diverges from existing mem0 conventions, for example doesn't use deserialize_secrets_inplace, warm_up() and client property, keyword-only arguments, ...

Thanks again for your efforts and good luck with setting up the integration under https://github.com/Dakera-AI!

@ferhimedamine

Copy link
Copy Markdown
Author

@julian-risch thx for comment and review, just curious what changed your mind , we were reviewing / working on the internal integration, now the recommendation is external integration

@julian-risch

Copy link
Copy Markdown
Member

It's a better fit with your other framework integrations. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:CI type:documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add dakera-haystack integration — self-hosted persistent memory for Haystack pipelines

3 participants