Python SDK
The jennah-sdk-py package provides Python bindings for the Jennah gRPC API, offering both synchronous and asynchronous clients.
Quickstart
Authenticate using jnh login (see CLI overview) or by setting the JENNAH_API_KEY environment variable. The following example creates an agent, stores a vector chunk, and performs semantic search:
import uuid
from jennah import Client
from jennah.agent.v1 import agent_pb2, memory_pb2
with Client() as client: # uses `jnh login` or $JENNAH_API_KEY
agent_id = f"quickstart-{uuid.uuid4().hex[:8]}"
client.agents.CreateAgent(
agent_pb2.CreateAgentRequest(agent_instance_id=agent_id)
)
agent = client.agent(agent_id)
# Store a vector chunk. Raw content is embedded automatically.
agent.vectors.upsert(memory_pb2.VectorChunk(
chunk_id="pref-1",
raw_content="The customer prefers invoices in Japanese yen, "
"sent on the 5th.",
))
# Semantic search query.
result = agent.vectors.search(memory_pb2.SemanticQuery(
query_text="what currency does the customer want to be "
"billed in?",
limit=3,
))
for m in result.matches:
print(f"{m.distance:.3f} {m.raw_content}")
client.agents.DeleteAgent(
agent_pb2.DeleteAgentRequest(agent_instance_id=agent_id)
)
Synchronous and asynchronous clients
Both Client and AsyncClient expose identical service stubs and methods:
| Client | Runtime | Example Call |
|---|---|---|
Client |
Scripts, notebooks, evaluation harnesses, and synchronous workflows | client.agents.ListAgents(req) |
AsyncClient |
Asyncio applications, web frameworks, and agent runtimes | await client.agents.ListAgents(req) |
AsyncClient is built on grpc.aio and handles non-blocking I/O for all RPCs, including background credential renewal. For asyncio programs, use AsyncClient directly rather than executing Client in a thread executor.
import asyncio
from jennah import AsyncClient
from jennah.agent.v1 import memory_pb2
async def main():
async with AsyncClient() as client:
query = memory_pb2.SemanticQuery(
query_text="billing preferences", limit=3
)
result = await client.agent("my-agent").vectors.search(query)
print(result.matches)
asyncio.run(main())
Service stubs and agent helpers
API services are accessible directly as stubs bound to the authenticated connection:
| Attribute | Service | Description |
|---|---|---|
agents |
AgentService |
Agent lifecycle management |
memory |
MemoryService |
Commit, query, inspect, form, and supersede memory operations |
scopes |
ScopeService |
Scope isolation and access control |
datasets, schema, data |
Datastore services | Application datastore management and queries |
auth |
AuthService |
Identity, organization members, API keys, and roles |
approvals |
ApprovalService |
Operation approvals workflow |
billing, platform |
Platform services | Subscriptions and platform locations |
To bind additional or newer gRPC stubs to the authenticated channel, pass the stub class to client.stub(SomeServiceStub).
Channel management
Manual gRPC channels bypass SDK credential resolution and token refresh interceptors. Use stubs provided by Client or AsyncClient.
Agent memory helpers
client.agent(id) provides convenience helpers for single-section memory operations:
| Helper | Operation |
|---|---|
memory.commit(**fields) |
Issues a CommitMemory request |
memory.query(**fields) |
Issues a QueryMemory request |
logs.create(step) / logs.recent(query) |
Execution log section operations |
vectors.upsert(*chunks) / vectors.search(query) |
Vector section operations |
graph.write(write) / graph.query(traversal) |
Graph section operations |
Authentication and credentials
Credentials are resolved in the following order of precedence:
credentials=: An explicit credentials provider instance.api_key=: An API key string.JENNAH_API_KEY: Environment variable.- Stored CLI session: Session credentials created by
jnh loginin~/.config/jennah/credentials.
When an access token expires during a request, the client automatically requests a renewed token and retries the call. The updated session is written back to ~/.config/jennah/credentials to keep CLI commands and other client instances synchronized.
client.credential returns metadata describing the active credential type and source without exposing sensitive secrets.
Error handling
Authentication and credential resolution errors raise dedicated SDK exceptions:
jennah.NoCredentialError: No credentials were found across configured sources.jennah.CorruptSessionError: Stored session file cannot be parsed.jennah.SessionExpiredError: Stored session has expired and cannot be refreshed, or the server rejected an explicitly supplied token.jennah.CredentialRefusedError: Server rejected the supplied API key.
Remote service failures raise grpc.RpcError. Use jennah.code(err) to extract gRPC status codes uniformly across SDK and gRPC exceptions.
Examples
- memchat (Python): Interactive chatbot built on
Clientdemonstrating semantic recall,FormMemorywith formation keys and deadlines, and formation receipt processing. (GitHub) - LangChain adapter: LangChain
BaseRetrieverand memory formation integration built onAsyncClient, featuring an example agent with asynchronous background memory formation. (GitHub)