osyra_ome.adapters.langchain
Private pre-release / network boundary: The currently usable customer surface is offline verification of an existing signed artifact against an out-of-band trust anchor. Memory/network RPC APIs and host snippets on this page describe protocol and interface shape; the public Edge bridge, package publication, and release acceptance remain pending. Do not treat
api.osyra.ai:443examples as a released customer route.
LangChain adapter — register OME as a memory + retriever backend.
Exports three classes per design:
- :class:`OmeChatMessageHistory` — modern (1.x) BaseChatMessageHistory
implementation. Pair with
RunnableWithMessageHistory. - :class:`OmeMemory` — forward-compat shim over ``OmeChatMessageHistory``
exposing the legacy 0.x BaseMemory
save_context/load_memory_variablessurface. - :class:`OmeVectorStore` — VectorStore decorator that audits every document insertion into OME while delegating similarity search to any underlying store (FAISS/Chroma/Pinecone/etc.)
Async variant:
- :class:`OmeAsyncChatMessageHistory` — pair with
:class:
osyra_ome.OmeAsyncClient.
Optional install::
pip install 'osyra-ome[langchain]'Importing this package fires :func:`check_framework_installed` and raises a guided ``ImportError`` if the required langchain / langchain-core extras are not present.
Classes
class OmeAsyncChatMessageHistory
Bases: BaseChatMessageHistory
Async LangChain chat-message-history backed by OME.
Args:
client: a :class:osyra_ome.OmeAsyncClient instance.
session_id: identifier for the conversation thread; encoded
into the subject_id of every emitted Claim.
workspace_id: OME workspace UUID-like id; if omitted, defers
to the workspace_id bound on the wrapped client.
verify_on_read: bind each replayed body to the claim SIGNATURE
(DEFAULT True). See the sync :class:OmeChatMessageHistory.
trusted_signer_key: caller-pinned 32-byte Ed25519 trust anchor;
REQUIRED when verify_on_read is True (H7).
Methods
__init__
def __init__(self, *, client: OmeAsyncClient, session_id: str, workspace_id: str | None = None, verify_on_read: bool = True, trusted_signer_key: bytes | None = None) -> Noneaadd_messages (async)
async def aadd_messages(self, messages: Sequence[BaseMessage]) -> NoneAsync persist each message as an OME Claim.
aclear (async)
async def aclear(self) -> Noneadd_messages
def add_messages(self, messages: Sequence[BaseMessage]) -> NoneSync convenience that errors out for the async client.
The base abstract requires a ``add_messages`` even on async consumers, so we satisfy the contract with a guard that surfaces a clear message rather than silently calling the async path on the wrong event loop.
aget_messages (async)
async def aget_messages(self) -> list[BaseMessage]Async replay of all messages written via this adapter.
Same replay semantics as the sync sibling's :attr:`messages`:
- ORDER (arch Low): WRITE order (iterating the in-process written-claim cache), NOT timestamp-sorted like the TS sibling — to be aligned when the v0.2 list-by-subject surface lands.
- SOURCE (arch Low): on the real v0.1 wire, ``ClaimProjection`` strips the body (``get_claim(claim_id).body == b""``), so production replay binds an empty body and FAILS CLOSED (returns no data, never forged data); the in-process write-cache is the only v0.1 preimage+body source. Full cross-instance replay needs the v0.2 preimage-bearing ``GetClaimBody`` (ENG-2225).
- Per-claim isolation (arch Nit): a signed-but-foreign claim with no ``"message"`` envelope key is SKIPPED, not a KeyError.
clear
def clear(self) -> Nonemessages @property
def messages(self) -> list[BaseMessage]Synchronous read of the cached message buffer.
Async LangChain consumers typically prefer :meth:`aget_messages`; this sync property is provided as a fallback for the parts of the LangChain runnables surface that touch ``.messages`` synchronously (a known wart in the framework). When the sync path is hit on an async adapter, we surface the locally cached buffer and DO NOT issue an RPC — that would deadlock on the running event loop.
session_id @property
def session_id(self) -> strworkspace_id @property
def workspace_id(self) -> strwritten_claim_ids @property
def written_claim_ids(self) -> tuple[str, ...]class OmeChatMessageHistory
Bases: BaseChatMessageHistory
LangChain chat-message-history backed by the OME memory namespace.
Args:
client: a :class:osyra_ome.OmeClient instance (sync).
session_id: identifier for the conversation thread; encoded
into the subject_id of every emitted Claim.
workspace_id: OME workspace UUID-like id; if omitted, defers
to the workspace_id bound on the wrapped client.
verify_on_read: when True (the DEFAULT), every replayed message
body is bound to the SIGNATURE-attested content hash of the
claim it was written under (ENG-2413). The adapter caches the
:class:~osyra_ome.types.SignedClaim it wrote (the v0.1-wire
source of the signing preimage) and re-binds the read-back body
to it; a tampered body fails CLOSED (raises). Set False ONLY to
opt out (you then trust the transport / verify out-of-band).
trusted_signer_key: caller-pinned 32-byte Ed25519 public key used as
the trust anchor for verify_on_read. REQUIRED when
verify_on_read is True — the in-band signer_pub is NOT a
trust anchor, so the constructor fails CLOSED (raises) without it
(H7 parity with the TS adapter).
Methods
__init__
def __init__(self, *, client: OmeClient, session_id: str, workspace_id: str | None = None, verify_on_read: bool = True, trusted_signer_key: bytes | None = None) -> Noneadd_messages
def add_messages(self, messages: Sequence[BaseMessage]) -> NonePersist each message as an OME Claim and audit the write.
clear
def clear(self) -> NoneForget the in-memory buffer of written claim ids.
v0.1 NOTE: this DOES NOT tombstone the underlying OME claims — tombstone is a v0.2 surface (``client.memory.tombstone``). To cryptographically erase the conversation thread, upgrade to v0.2 and pair :meth:`clear` with a follow-up loop over :attr:`written_claim_ids` calling tombstone() with reason "langchain.adapter.clear".
Bookkeeping: clears the cached read-back and the per-instance buffer so subsequent reads on this adapter start from empty.
messages @property
def messages(self) -> list[BaseMessage]Replay all messages written via this adapter instance.
v0.1 surface: re-fetches each written claim from the OME memory namespace and reconstitutes the LangChain message via :func:`messages_from_dict`. Cached after the first call; invalidated on :meth:`add_messages` and :meth:`clear`.
Replay ORDER (arch Low): messages come back in WRITE order — this iterates the in-process written-claim cache (:attr:`written_claim_ids`), it does NOT timestamp-sort like the TS sibling. To be aligned when the v0.2 list-by-subject surface lands (ENG-2225 ``GetClaimBody``); see the wire-source note below.
Replay SOURCE (arch Low): on the real v0.1 wire, ``ClaimProjection`` STRIPS the body — ``get_claim(claim_id).body`` is ``b""`` — so a production replay binds an EMPTY body and FAILS CLOSED (returns NO data, never forged data), because the signature-attested ch of a real message never equals ``SHA-256(b"")``. The in-process write-cache populated by :meth:`add_messages` is therefore the ONLY v0.1 preimage+body source. Full cross-instance replay needs the v0.2 preimage-bearing ``GetClaimBody`` (ENG-2225).
Per-claim isolation (arch Nit): a genuinely-signed-but-foreign claim whose decoded envelope lacks the adapter ``"message"`` key is SKIPPED (post-binding, NOT a forge vector), not a KeyError that aborts the whole replay — see :func:`_extract_serialized_message`.
session_id @property
def session_id(self) -> strThe user-supplied LangChain session identifier.
workspace_id @property
def workspace_id(self) -> strThe OME workspace id the chat history binds to.
written_claim_ids @property
def written_claim_ids(self) -> tuple[str, ...]The list of claim ids this adapter instance has written so far.
class OmeMemory
Conversation-buffer-memory façade over :class:`OmeChatMessageHistory`.
Args:
client: a :class:osyra_ome.OmeClient.
session_id: identifier for the conversation thread.
memory_key: the key under which the buffered history is
returned by :meth:load_memory_variables. Defaults to
"history" to match the standard LangChain convention.
input_key: key in save_context inputs that carries the
user's utterance. Defaults to "input".
output_key: key in save_context outputs that carries the
assistant's response. Defaults to "output".
return_messages: when True, :meth:load_memory_variables
returns a list of :class:BaseMessage objects; when False,
it returns a single concatenated string (the LangChain 0.x
default). Defaults to True.
workspace_id: OME workspace UUID-like id; if omitted, defers to
the workspace_id bound on the wrapped client.
verify_on_read: bind each replayed body to the claim SIGNATURE
(DEFAULT True; forwarded to the wrapped
:class:OmeChatMessageHistory). See its docstring (ENG-2413).
trusted_signer_key: caller-pinned 32-byte Ed25519 trust anchor;
REQUIRED when verify_on_read is True (forwarded; H7).
Methods
__init__
def __init__(self, *, client: OmeClient, session_id: str, memory_key: str = 'history', input_key: str = 'input', output_key: str = 'output', return_messages: bool = True, workspace_id: str | None = None, verify_on_read: bool = True, trusted_signer_key: bytes | None = None) -> Nonechat_memory @property
def chat_memory(self) -> AnyThe wrapped chat-message-history (for direct access).
Returns the ``OmeChatMessageHistory`` instance — typed as ``Any`` because the class is built dynamically by a factory and mypy doesn't follow that pattern. Callers can rely on the runtime attributes documented on :class:`OmeChatMessageHistory`.
clear
def clear(self) -> NoneForget the in-memory buffer of written claim ids.
Mirrors :meth:`OmeChatMessageHistory.clear` — does NOT tombstone the underlying claims (v0.2 surface).
load_memory_variables
def load_memory_variables(self, inputs: dict[str, Any]) -> dict[str, Any]Return the buffered conversation under :attr:`memory_key`.
``inputs`` is part of the LangChain 0.x BaseMemory protocol signature; unused here but accepted for API compatibility.
memory_variables @property
def memory_variables(self) -> list[str]LangChain BaseMemory protocol — the keys this memory exposes.
save_context
def save_context(self, inputs: dict[str, Any], outputs: dict[str, Any]) -> NonePersist the latest user turn + assistant turn into OME.
Pulls ``inputs[input_key]`` + ``outputs[output_key]`` and writes two Claims (HumanMessage + AIMessage) via the underlying chat history. Raises KeyError if either field is missing.
session_id @property
def session_id(self) -> strclass OmeVectorStore
Bases: VectorStore
VectorStore audit-decorator pairing OME with a delegate index.
Args:
client: OmeClient bound to the destination workspace.
delegate: any LangChain :class:VectorStore implementation
used for the actual similarity search backend.
workspace_id: OME workspace identifier. If omitted, defers
to the workspace_id bound on the wrapped client.
collection: logical collection name; encoded into the
subject_id of every emitted Claim
(did:web:collection.osyra.ai/<collection>).
Methods
__init__
def __init__(self, *, client: OmeClient, delegate: VectorStore, workspace_id: str | None = None, collection: str = 'default') -> Noneadd_texts
def add_texts(self, texts: Iterable[str], metadatas: list[dict[str, Any]] | None = None, **kwargs: Any) -> list[str]Persist texts to the delegate AND to OME as Claims.
Returns the list of IDs from the delegate (not OME claim ids) so callers expecting LangChain VectorStore semantics still work. The OME claim ids are recorded on :attr:`written_claim_ids` for audit-trail access.
delegate @property
def delegate(self) -> VectorStoreThe underlying VectorStore (FAISS/Chroma/Pinecone/etc).
delete
def delete(self, ids: list[str] | None = None, **kwargs: Any) -> bool | Noneembeddings @property
def embeddings(self) -> Embeddings | Nonefrom_texts @classmethod
def from_texts(cls, texts: list[str], embedding: Embeddings, metadatas: list[dict[str, Any]] | None = None, **kwargs: Any) -> OmeVectorStoreConstruct via a delegate's from_texts.
Required kwargs: client: OmeClient delegate_cls: VectorStore subclass to use for the index workspace_id (optional) collection (optional; default "default")
Other kwargs are forwarded to the delegate's from_texts.
similarity_search
def similarity_search(self, query: str, k: int = 4, **kwargs: Any) -> list[Document]written_claim_ids @property
def written_claim_ids(self) -> tuple[str, ...]