Skip to content

Add Memory CRUD methods + runs methods#2

Merged
cdpierse merged 6 commits intomainfrom
add-memory-crud
Feb 20, 2026
Merged

Add Memory CRUD methods + runs methods#2
cdpierse merged 6 commits intomainfrom
add-memory-crud

Conversation

@cdpierse
Copy link
Collaborator

Implements the core Python SDK client for the Engram memory API, covering all five endpoints:

  • client.memories.add() — create memories from plain text, conversations, or pre-extracted content
  • client.memories.get() / delete() — fetch or remove a specific memory
  • client.memories.search() — vector, BM25, or hybrid search with configurable retrieval
  • client.runs.get() / wait() — check pipeline run status or poll until completion

Both sync (EngramClient) and async (AsyncEngramClient) clients are supported with identical APIs. The content serialization matches the backend's type-discriminated envelope format ({type: "string", content: ...}).

@cdpierse cdpierse self-assigned this Feb 16, 2026
Copy link

@orca-security-eu orca-security-eu bot left a comment

Choose a reason for hiding this comment

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

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Infrastructure as Code high 0   medium 0   low 0   info 0 View in Orca
Passed Passed SAST high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

@cdpierse cdpierse requested a review from a team February 17, 2026 10:51
Comment on lines +56 to +60
raise AuthenticationError(detail, status_code=401, body=data)

if response.status_code == 404:
detail = _extract_detail(data, "Not found")
raise NotFoundError(detail, status_code=404, body=data)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could probably make the status_code default in these errors since they should always be consistent

from dataclasses import dataclass, field
from typing import Literal

# ── Request models ──────────────────────────────────────────────────────
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're needing to split this file up with comments like this, we should probably just split them into separate files now

SearchResults,
)

# ── Body builders ───────────────────────────────────────────────────────
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to before, these should be separate files not separated by comments

self.memories = AsyncMemories(self._transport)
self.runs = AsyncRuns(self._transport)

def build_request(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this needed anywhere now (are we always using the client.subclient.method() structure)? It might be cleaner to remove this and enforce that each subclient has to own a reference to the transport

Comment on lines +56 to +58
async def aclose(self) -> None:
if self._owns_http_client:
await self._http_client.aclose()
await self._transport._http_client.aclose()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine, but now that we have the HttpTransport classes, I wonder whether this check belongs in there? So, make AsyncHttpTransport take an init arg http_client: httpx.AsyncClient | None and keep the _owns_http_client abstracted away in there? This can just be a AsyncHttpTransport.close() method then (which is either no-op or ._http_client.aclose() depending on whether the transport owns the client or not)

@@ -24,4 +23,4 @@
*,
base_url: str = DEFAULT_BASE_URL,
api_key: str | None = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

shouldn't api key be required?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep

*,
base_url: str = DEFAULT_BASE_URL,
api_key: str | None = None,
headers: Mapping[str, str] | None = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

what would users pass in headers that we expose them?

Copy link
Collaborator

Choose a reason for hiding this comment

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

we sure we wanna allow passing http client? Seems like implementation details of the client, and later it's harder to refactor if it can be passed by users

detail = _extract_detail(data, "Authentication failed")
raise AuthenticationError(detail, body=data)

if response.status_code == 404:
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need this granular error handling, maybe just APIError for all so we don't need to maintain this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think AuthenticationError feels okay as a distinct category of error, agree though that we don't want to go too granular beyond that.


run_id: str
status: str
error: str | None = None
Copy link
Collaborator

Choose a reason for hiding this comment

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

just to confirm, is run.error user-facing? If yes, then we should be more careful what we put there in server

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this will be the error that we surface to developer users.

@cdpierse cdpierse merged commit 30f59b1 into main Feb 20, 2026
11 checks passed
@cdpierse cdpierse deleted the add-memory-crud branch February 20, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants