Python Conversations Client Tutorial¶
This tutorial shows how to use the moraine-conversations Python binding as a high-level read-only client for Moraine conversation data. The binding exports ConversationClient and provides three primary methods: list_conversations_json, get_conversation_json, and search_conversations_json. init.py:L1-3lib.rs:L62-140
Prerequisites¶
You need a running Moraine stack (or any reachable ClickHouse endpoint with the Moraine schema) and Python 3.9+. A standard local setup is to start the stack with bin/moraine up. The binding package itself is configured for Python >=3.9 and uses maturin as the build backend. build-and-operations.md:L89-104pyproject.toml:L1-10
Install The Binding Locally¶
From the repo root:
cd bindings/python/moraine_conversations
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop
At that point, import moraine_conversations loads the compiled extension module in your virtualenv. README.md:L5-17pyproject.toml:L20-23
Create A Client¶
from moraine_conversations import ConversationClient
client = ConversationClient(
url="http://127.0.0.1:8123",
database="moraine",
timeout_seconds=5.0,
)
The constructor accepts url, database, username, password, timeout_seconds, and max_results. lib.rs:L18-60
List Conversations By Date And Mode¶
import json
page = json.loads(
client.list_conversations_json(
from_unix_ms=1767261600000,
to_unix_ms=1767500000000,
mode="web_search",
limit=50,
cursor=None,
)
)
for convo in page["items"]:
print(convo["session_id"], convo["last_event_time"], convo["mode"])
next_cursor = page["next_cursor"]
mode must be one of web_search, mcp_internal, tool_calling, or chat. Modes are computed per session (exactly one mode per session), using first-match precedence over any matching event: web_search > mcp_internal > tool_calling > chat. In concrete terms: web_search covers web search events (web_search_call, search_results_received, or tool_use with WebSearch/WebFetch), mcp_internal covers Codex MCP internal search/open activity, tool_calling covers remaining tool activity (tool_call, tool_result, tool_use), and chat means none of those signals were present. Results are paginated with next_cursor. lib.rs:L62-85lib.rs:L148-155clickhouse_repo.rs:L451-471
Retrieve One Conversation By ID¶
import json
conversation = json.loads(client.get_conversation_json("sess_a", include_turns=True))
if conversation is None:
print("not found")
else:
print(conversation["summary"]["session_id"])
print(len(conversation["turns"]))
The method returns JSON for Option<Conversation>, so a missing session is null in Python after json.loads. lib.rs:L87-98
Search Conversations (Whole-Conversation Ranking)¶
import json
results = json.loads(
client.search_conversations_json(
query="vector store migration",
limit=10,
min_score=0.0,
min_should_match=1,
from_unix_ms=1767261600000,
to_unix_ms=1767500000000,
mode="chat",
include_tool_events=True,
exclude_codex_mcp=False,
)
)
for hit in results["hits"]:
print(hit["session_id"], hit["score"], hit.get("best_event_uid"))
This search is ranked at the conversation/session level, not only single events. Internally, event scores are aggregated by session_id, and the best event per session is carried through as best_event_uid plus a snippet. lib.rs:L100-140clickhouse_repo.rs:L592-620
Run The Python Smoke Test¶
cd bindings/python/moraine_conversations
source .venv/bin/activate
pip install pytest
CARGO_HOME=/tmp/cargo-home maturin develop
pytest -q tests/test_smoke.py
The smoke test exercises list/get/search end-to-end against a mock ClickHouse HTTP endpoint. test_smoke.py:L120-166pyproject.toml:L17-18
Error Semantics¶
Invalid mode values raise a Python ValueError. Backend or query failures are surfaced as Python RuntimeError values with the underlying error text. lib.rs:L7-8lib.rs:L153-160