Tex Python SDK for IntegrationBackend
Project description
Tex SDK (Python)
Tex is a lightweight Python client for Tex’s IntegrationBackend HTTP API.
This README is intentionally implementation-grounded: it documents exactly what the SDK does today based on the code in:
tex/__init__.py(public exports)tex/config.py(endpoint paths + config)tex/client.py(client behavior, auth, retries, request/response handling)tex/errors.py(error types)
If you are reading this inside the repo, those files are the source of truth.
What you get
- One client class:
Tex - Three auth modes (API key, org+user login, or direct access token)
- Minimal, predictable return values: most methods return JSON as
dict - Friendly NLQ output via
AskResult - Typed package (
tex/py.typed) for mypy/pyright
Install
pip install tex-sdk
Optional: enable HTTP/2 (requires h2):
pip install "tex-sdk[http2]"
Python requirement (from pyproject.toml): Python 3.9+
Quick start
Important: the SDK’s base_url must point at IntegrationBackend (the FastAPI gateway), not HelixDB directly.
1) API key auth (recommended)
This is the simplest and most “production-like” flow.
from tex import Tex
tx = Tex("http://localhost:8000", api_key="sk_live_...")
tx.store_memory(
"Hello from Tex SDK.",
type="document",
metadata={"source": "quickstart"},
)
answer = tx.ask("What did I just store?")
print(answer.text)
2) Org + user login (dev / testing)
If your IntegrationBackend allows /auth/login for a given org+user, you can use:
from tex import Tex
tx = Tex(
"http://localhost:8000",
org_id="org_123",
user_id="user_456",
session_id="s1", # optional
)
tx.store_memory("I like espresso.", type="document")
print(tx.ask("What do I like?").text)
3) Direct access token (bring-your-own JWT)
If you already obtained an access token (e.g., out-of-band), pass it directly:
from tex import Tex
tx = Tex("http://localhost:8000", access_token="eyJ...")
print(tx.whoami())
The public API surface
The package exports (see tex/__init__.py):
TexAskResultTexConfig,TexEndpointsTexError,TexAuthError,TexHTTPError
Concepts
Multi-tenant scope
IntegrationBackend is multi-tenant. Requests typically belong to a tenant:
org_iduser_idsession_id(optional)
In the SDK, “scope” is included in ingestion payloads via Tex._scope_payload(). Internally:
- The backend ultimately trusts the JWT for tenant claims.
- The SDK still sends
scopebecause the request models expect it. - When using API keys, the SDK attempts to discover tenant claims by calling
GET /auth/verify.
If tenant discovery fails, the SDK may send placeholders ("_") for org_id/user_id; the backend is expected to ignore these and use the JWT tenant instead.
Correlation IDs
Each request includes a unique X-Correlation-ID header generated per request (see tex/client.py).
- This is useful for tracing requests through your logs.
- When available, the SDK also exposes this as
request_idon raised exceptions.
Authentication: exact behavior
This section mirrors Tex._ensure_auth() and Tex._refresh() in tex/client.py.
Auth modes (in priority order)
When the SDK needs auth, it chooses the first available:
- If
access_tokenis already present in memory → use it. - If
TexConfig.access_tokenis provided → use it. - Else if
api_keyis provided:POST /auth/token-exchangewith{ "api_key": "..." }→ getaccess_token(+ optionalrefresh_token)GET /auth/verifyto discover tenant claims- If you also explicitly provided
user_id, the SDK then callsPOST /auth/loginto mint a user-scoped token.
- Else if
org_id+user_idis provided:POST /auth/loginwith{ org_id, user_id, session_id }
- Otherwise → raise
TexAuthError(“No auth configured...”).
Refresh + retry
All authenticated requests go through _request().
- If a request returns HTTP 401:
- The SDK attempts
_refresh()and retries the request once.
- The SDK attempts
- Refresh rules:
- If
refresh_tokenexists:POST /auth/refresh. - Else if
api_keyexists: re-exchange the API key (and, ifuser_idwas set, re-login). - Otherwise: raise
TexAuthError.
- If
Configuration
TexConfig
You can construct the client with either:
- A
base_urlstring:Tex("http://localhost:8000", api_key=...) - A
TexConfigobject:Tex(TexConfig(base_url=..., api_key=...))
Fields (see tex/config.py):
base_url: IntegrationBackend URL, e.g.http://localhost:8000- Auth fields:
api_key,org_id,user_id,session_id,access_token,refresh_token - Transport:
timeout_s(default 15s),http2(default True) endpoints: aTexEndpointsinstance
TexEndpoints
TexEndpoints contains the path strings the SDK calls (all relative to base_url).
Defaults (see tex/config.py):
- Auth:
auth_login:/auth/loginauth_refresh:/auth/refreshauth_token_exchange:/auth/token-exchangeauth_verify:/auth/verify
- Ingestion:
ingestion_document:/ingestion/documentingestion_episode:/ingestion/episodeingestion_preference:/ingestion/preferenceingestion_status:/ingestion/status/{job_id}ingestion_batch:/ingestion/batch
- DB:
db_query:/helixdb/querydb_schema:/helixdb/schema
- NLQ:
nlq_execute:/nlq/execute - Search:
search:/search - Memories CRUD:
memories_list,memories_get,memories_delete,memories_update - Episodes:
episodes_list:/memories/episodes - Users:
user_profile:/users/profile
If your deployment uses different routes/prefixes, override:
from tex import Tex, TexConfig, TexEndpoints
endpoints = TexEndpoints(
# example override
db_query="/db/query",
db_schema="/db/schema",
)
tx = Tex(TexConfig(base_url="https://api.example.com", api_key="sk_live_...", endpoints=endpoints))
API reference (method-by-method)
All examples assume:
from tex import Tex
tx = Tex("http://localhost:8000", api_key="sk_live_...")
Tex.store_memory(...)
Single ingestion entrypoint. Behavior depends on type.
Signature (from tex/client.py):
content:strorlist[dict](depending ontype)type: one of"document","episode","preference"format: for documents, default"text"metadata: optional dictoptions: optional dict (passed through)episode_id: only for episode ingestion
Document ingestion (type="document")
resp = tx.store_memory(
"A short document to ingest.",
type="document",
format="text",
metadata={"source": "docs"},
)
print(resp)
Notes:
- For
type="document",contentmust be a string or the SDK raisesValueError. - The request payload includes
scopederived from your auth.
Episode ingestion (type="episode")
Episodes represent chat-like messages.
You can pass a single string (it becomes one {"role":"user","content":...} message):
resp = tx.store_memory(
"Today I called Alice and discussed the plan.",
type="episode",
)
Or pass a message list:
messages = [
{"role": "user", "content": "Book a table for two."},
{"role": "assistant", "content": "Which restaurant?"},
{"role": "user", "content": "Somewhere near downtown."},
]
resp = tx.store_memory(messages, type="episode", episode_id="ep_001")
If you pass an invalid structure, the SDK raises ValueError.
Preference ingestion (type="preference")
Preference ingestion expects preferences in metadata["preferences"].
resp = tx.store_memory(
"ignored",
type="preference",
metadata={
"preferences": [
{"key": "drink", "value": "espresso", "confidence": 0.9},
]
},
)
Important:
- For
type="preference", the SDK ignorescontentand requires a non-empty list atmetadata["preferences"]. - The SDK sends
{ org_id, user_id, session_id, preferences }using discovered scope.
Tex.job(job_id)
Fetch background ingestion status.
status = tx.job("job_...")
print(status)
Internally calls GET /ingestion/status/{job_id}.
Tex.batch_store(documents)
Ingest multiple documents in one call.
Each document dict should contain:
data(str)- optional
format,metadata,options
resp = tx.batch_store(
[
{"data": "doc 1", "metadata": {"source": "batch"}},
{"data": "doc 2", "format": "text"},
]
)
print(resp)
Return shape depends on IntegrationBackend; the SDK returns the JSON response.
Tex.search(query, ...)
Fast semantic search.
resp = tx.search("espresso", top_k=5)
print(resp)
Optional parameters:
min_score: floatlabel: string label filtermetadata_filter: dict
Tex.ask(question, ...) → AskResult
Natural language query execution.
ans = tx.ask("What did I store recently?")
print(ans.text)
Return type is AskResult:
text: human-readable summary assembled from evidence/bindings/documentsevidence: list of evidence stringsentities: list of extracted/bound entity namesdocuments: list of document identifiersraw: the full JSON response dict
Parameters (passed through to /nlq/execute):
execute(default True)enable_pruning(default True)use_local_intelligence(default True)intent_match_options(optional dict)compact(default True)
Tex.query(query, params=None)
Execute a DB query via IntegrationBackend.
resp = tx.query(
"MATCH (n) RETURN n LIMIT $limit",
params={"limit": 5},
)
print(resp)
This calls POST /helixdb/query by default (see TexEndpoints.db_query).
Tex.schema()
Fetch the database schema.
schema = tx.schema()
print(schema)
This calls GET /helixdb/schema by default.
Tex.whoami()
Returns the tenant context as seen by IntegrationBackend (great for debugging auth/scope).
print(tx.whoami())
Internally, it ensures auth, then calls GET /auth/verify (unless already cached).
Memories CRUD
These map to /memories endpoints.
Tex.get_memory(memory_id)
mem = tx.get_memory("mem_...")
print(mem)
Tex.list_memories(type=None, limit=50, offset=0)
resp = tx.list_memories(limit=20, offset=0)
print(resp)
docs = tx.list_memories(type="document", limit=20)
print(docs)
Tex.update_memory(memory_id, content=None, metadata=None, options=None)
resp = tx.update_memory(
"mem_...",
content="updated content",
metadata={"tag": "updated"},
)
print(resp)
Tex.delete_memory(memory_id)
resp = tx.delete_memory("mem_...")
print(resp)
Tex.delete_memories(user_id=None)
Bulk delete.
- If
user_idis omitted, deletes the caller’s memories. - If
user_idis provided and differs from caller, IntegrationBackend may require elevated roles.
resp = tx.delete_memories()
print(resp)
Episodes
Tex.list_episodes(limit=50, offset=0, since=None)
resp = tx.list_episodes(limit=10)
print(resp)
User profile
Tex.get_profile(format="text")
Returns a synthesized profile derived from preferences and episodic memories.
profile = tx.get_profile(format="text")
print(profile)
Errors and exception handling
Errors are defined in tex/errors.py.
TexHTTPError
Raised for non-auth HTTP failures (status >= 400 excluding 401/403) and network/timeout errors.
Fields:
message(str)status_code(optional int)request_id(optional str; pulled fromX-Correlation-IDresponse header if present)response_text(optional str; truncated to 2000 chars)details(any; parsed JSON when possible)
TexAuthError
Subclass of TexHTTPError, raised for auth issues:
- “No auth configured”
- token exchange/login failures
- HTTP 401/403 responses
Typical pattern
from tex import Tex, TexAuthError, TexHTTPError
tx = Tex("http://localhost:8000", api_key="sk_live_...")
try:
print(tx.whoami())
except TexAuthError as e:
# Wrong key, missing roles, expired/invalid refresh, etc.
print("auth failed", str(e), e.status_code)
except TexHTTPError as e:
# Non-auth HTTP errors (422, 500, network issues)
print("request failed", str(e), e.status_code)
Request/response handling details
This mirrors Tex._request() and _post_noauth().
- JSON responses are returned as Python dicts.
- If the response body is valid JSON but not a dict (e.g., a list), the SDK wraps it as
{ "data": <payload> }. - If the response is not JSON, the SDK returns
{ "data": "<text>" }. - For error responses, the SDK tries to extract
detailormessagefrom JSON. Otherwise: “Request failed”.
Special hint for HTTP 422:
- If the error mentions “IntentGraph validation” / “root variable must have a type”, the SDK appends a hint suggesting schema/planner issues.
Running the smoketest script (repo)
The repo includes a minimal end-to-end tester:
tools/tex_sdk_smoketest.py
It is designed to work:
- When installed from PyPI (
pip install tex-sdk) - When run directly from this repo (it falls back to adding the repo root to
sys.path)
Environment variables
TEX_BASE_URL(default:http://localhost:8000)TEX_TIMEOUT_S(default:30)
Auth (set exactly one of the following groups):
- API key:
TEX_API_KEY=sk_live_...
- Org+user login:
TEX_ORG_ID=...TEX_USER_ID=...TEX_SESSION_ID=...(optional)
- Direct token:
TEX_ACCESS_TOKEN=eyJ...
Run
$env:TEX_BASE_URL = "http://localhost:8000"
$env:TEX_API_KEY = "sk_live_..."
python tools/tex_sdk_smoketest.py
The script performs:
GET /health(unauth quick check)whoami()store_memory(type="document")- polls
job(job_id)(if job_id returned) search(...)ask(...)
Migration notes (in-repo users)
Inside this repo, there is also an sdk/ package that re-exports everything from tex/ as a compatibility shim.
- New code should import from
tex. - Old code importing
sdkwill continue to work (seesdk/__init__.py).
Troubleshooting
“Connection refused” / health check fails
- Ensure IntegrationBackend is running (default:
http://localhost:8000/health). - The SDK does not start services; it only calls HTTP endpoints.
HTTP 401 / 403
- Verify you’re using the correct auth mode.
- For API key auth: ensure the key is valid and belongs to the tenant you expect.
- For org+user login: ensure
/auth/loginis enabled for that org/user.
HTTP 422 (validation errors)
- Usually means your request payload doesn’t match backend expectations.
- For NLQ/planner-related validation errors, confirm the DB schema is available and the planner can fetch it.
HTTP/2 import error (h2)
If you see an ImportError mentioning h2, install:
pip install "tex-sdk[http2]"
Or disable HTTP/2:
from tex import Tex
tx = Tex("http://localhost:8000", api_key="sk_live_...", http2=False)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tex_sdk-0.3.0.tar.gz.
File metadata
- Download URL: tex_sdk-0.3.0.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb188aba1fb5ae1c80b2cfe035fb2679bdf38e0c74540139abb5cf726473fe01
|
|
| MD5 |
b39876bce1dc9fbffaed5de2cb48cc53
|
|
| BLAKE2b-256 |
6d9ada87b8fd4c683bd3744acc10e2189d326f334ea0e1cd1339a32339bb6d80
|
File details
Details for the file tex_sdk-0.3.0-py3-none-any.whl.
File metadata
- Download URL: tex_sdk-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff7e4fce08b173153324d51e22ec7fd86d69e10aaa2624017ab617117239e216
|
|
| MD5 |
517557c7ef951627cbaea4d061fb2b9d
|
|
| BLAKE2b-256 |
094ca3e93a808ad8652119a92454667abaa8f7b8f863369012c817785de91411
|