Skip to main content

memoturn Python SDK — tracing, @observe, OpenAI wrapper, prompts.

Project description

memoturn Python SDK

Tracing, prompts, datasets, guardrails, and provider wrappers for memoturn. Stdlib-only — zero required dependencies.

pip install memoturn        # or: uv add memoturn

Optional extras (discoverability only — the SDK itself never imports them at runtime):

pip install "memoturn[openai]"      # openai>=1.0 for wrap_openai
pip install "memoturn[anthropic]"   # anthropic>=0.30 for wrap_anthropic
pip install "memoturn[langchain]"   # langchain-core for MemoturnCallbackHandler
pip install "memoturn[otel]"        # OTel SDK + OTLP/HTTP exporter for span_exporter/span_processor

Configuration

Every helper resolves credentials from arguments first, then environment variables:

Env var Default Used for
MEMOTURN_BASE_URL http://localhost:3001 API origin
MEMOTURN_PUBLIC_KEY / MEMOTURN_SECRET_KEY (empty) Basic-auth API key pair
MEMOTURN_ENVIRONMENT default environment stamped on events
MEMOTURN_MAX_BUFFER_SIZE 10000 event buffer cap
MEMOTURN_ALLOW_HTTP (unset) 1 suppresses the cleartext-http warning

Memoturn(...) constructor options:

mt = Memoturn(
    base_url="https://api.example.com",  # default: MEMOTURN_BASE_URL
    public_key="pk-...",                 # default: MEMOTURN_PUBLIC_KEY
    secret_key="sk-...",                 # default: MEMOTURN_SECRET_KEY
    environment="production",            # default: MEMOTURN_ENVIRONMENT or "default"
    flush_at=20,                         # auto-flush when the buffer reaches this many events
    max_buffer_size=10_000,              # hard cap; new events are dropped once reached
    request_timeout=10.0,                # per-request timeout, seconds
    mask=None,                           # redaction hook: mask(value, field, event_type)
    allow_insecure_http=False,           # suppress the http-to-non-local-host warning
)

Trace with the decorator

from memoturn import Memoturn, configure, observe

configure(Memoturn())  # or rely on env vars; get_client() returns the same default

@observe()
def retrieve(q): ...

@observe(as_type="generation")
def answer(q, docs): ...

@observe(name="rag-pipeline")
def rag(q):
    return answer(q, retrieve(q))   # nested spans under one trace

The outermost @observe opens a trace; nested calls (sync or async) become child spans. configure(client) sets the default client; get_client() returns it (creating an env-configured one on first use).

Low-level client

mt = Memoturn()
trace = mt.trace(name="chat", userId="u1", sessionId="s1", tags=["prod"])

gen = trace.generation(name="answer", model="claude-sonnet-4-5", input=messages)
gen.end(output=reply, usage={"promptTokens": 100, "completionTokens": 20, "totalTokens": 120})

span = trace.span(name="retrieve", input=query)      # spans nest: span.span(), span.generation(), ...
span.end(output=docs)

tool = trace.tool(name="web-search", input=query)    # classified TOOL in the console
tool.end(output=results)
step = trace.agent(name="planner", input=state)      # classified AGENT
step.end(output=plan)

trace.event(name="cache-hit", metadata={"key": "k1"})   # point-in-time event
trace.score("user-feedback", value=1, comment="helpful")

mt.flush()      # send now; raises on failure (transient failures re-buffer first)
mt.shutdown()   # flush + unregister the atexit hook — call before process exit

trace(...) kwargs: id, name, userId, sessionId, input, output, metadata, tags, environment, release, version. Span/generation kwargs are listed on their docstrings.

OpenAI wrapper

from openai import OpenAI
from memoturn import wrap_openai

client = wrap_openai(OpenAI())
client.chat.completions.create(model="gpt-4o-mini", messages=[...])  # recorded automatically
client.responses.create(model="gpt-4o-mini", input="hi")             # Responses API too

Pass wrap_openai(client, mt) to use a specific Memoturn instance, or wrap_openai(client, trace=trace) to nest all calls under an existing trace.

Anthropic wrapper

from anthropic import Anthropic
from memoturn import wrap_anthropic

client = wrap_anthropic(Anthropic())
client.messages.create(
    model="claude-sonnet-4-5",
    system="be terse",
    max_tokens=256,
    messages=[{"role": "user", "content": "2+2?"}],
)  # recorded: system + messages as input, usage incl. cache read/creation tokens

Same memoturn=/trace= options as wrap_openai. Streaming calls (stream=True) pass through unrecorded.

LangChain

from memoturn import MemoturnCallbackHandler

chain.invoke(inputs, config={"callbacks": [MemoturnCallbackHandler()]})

Records chains, LLM/chat-model calls (with token usage), and tools as a trace tree. Duck-typed — imports no LangChain packages.

Prompts

from memoturn import get_prompt, compile_prompt

prompt = get_prompt("support-reply", channel="production")
messages = compile_prompt(prompt, product="memoturn", question="How do I trace a call?")

If the channel runs an A/B split, pass a stable bucket_key (session/user id) so the caller sticks to one arm; stamp the returned prompt["version"] on your generation to attribute scores to the arm.

Datasets & CI quality gates

from memoturn import add_dataset_items, create_dataset, evaluate_gate, get_dataset, record_run

create_dataset("qa-regression", "golden Q&A set")
add_dataset_items("qa-regression", [{"input": "q1", "expectedOutput": "a1"}])

ds = get_dataset("qa-regression")
links = []
for item in ds["items"]:
    trace = mt.trace(name="eval-run", input=item["input"])
    # ... run your pipeline, end observations ...
    links.append({"datasetItemId": item["id"], "traceId": trace.id})
mt.flush()
record_run("qa-regression", "run-2026-07-16", links)

# Gate the run in CI — exit non-zero when quality regresses:
gate = evaluate_gate(
    "qa-regression",
    "run-2026-07-16",
    {"faithfulness": {"min": 0.8}, "toxicity": {"max": 0.1}},
    baseline_run="run-2026-07-09",  # enables "maxRegression" bounds
)
assert gate["passed"], gate["failures"]

Guardrails

from memoturn import check_guardrails

result = check_guardrails(user_input)
if result["verdict"] == "block":
    ...
elif result["verdict"] == "redact":
    user_input = result["redactedText"]

Scans text against the project's runtime guardrails (PII, prompt injection, blocked terms). Verdict is "allow", "redact", or "block".

OpenTelemetry

Already instrumented with OTel? Point it at memoturn's OTLP/HTTP receiver:

from memoturn.otel import otlp_config, span_exporter, span_processor

cfg = otlp_config()  # {"endpoint": ".../v1/otel/v1/traces", "headers": {"Authorization": "Basic ..."}}
# dependency-free: pass into any OTLP/HTTP exporter yourself, or:

provider.add_span_processor(span_processor())  # needs: pip install "memoturn[otel]"
exporter = span_exporter()                     # just the exporter, bring your own processor

GenAI semantic-convention attributes (gen_ai.*) map to traces + generations.

Production notes

See BEST_PRACTICES.md for HTTPS/key handling, flushing and buffer behavior, PII masking, timeouts, environments, and CI gating guidance.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

memoturn-0.3.0.tar.gz (127.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

memoturn-0.3.0-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file memoturn-0.3.0.tar.gz.

File metadata

  • Download URL: memoturn-0.3.0.tar.gz
  • Upload date:
  • Size: 127.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for memoturn-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e983213167d2489a5616fcea8873970dd9c31f8dfe9bb265ff255959f61a28cc
MD5 804e15f6e43d1b368d66db0c59ca7640
BLAKE2b-256 aebbeea4c4460698955c96007651b4872a9482c07e3d50fe71ec9759e8acb103

See more details on using hashes here.

File details

Details for the file memoturn-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: memoturn-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for memoturn-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b60d7fcada586c9e3d67fe3bcfa5ceab5961b18e6fd726eb4b1914dd462477ac
MD5 e97e5bc406be0479ecced062f2461904
BLAKE2b-256 9d3c9eda5025434c63f3934a6d59432e58b192903f0df14e5b98dba6d6b2f722

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page