Skip to content

latent.studio_client

HTTP client for Agent Studio API.

Classes

ConversationListResponse

ConversationListResponse()

Paginated response from the conversations endpoint.

ConversationSummary

ConversationSummary()

Conversation metadata from the list endpoint, used in eval report summaries.

DatasetSummary

DatasetSummary()

Dataset metadata returned by the list endpoint.

StudioClient

StudioClient(base_url: str | None = None, api_key: str | None = None)

Thin HTTP client for Agent Studio API.

Methods

ConversationSummary.message_count

ConversationSummary.score

ConversationSummary.status

StudioClient.create_dataset

create_dataset(slug: str, display_name: str, type_: DatasetType, source: DatasetSource = 'upload', description: str | None = None, tags: list[str] | None = None) -> str

Create a new empty dataset via POST /api/datasets/.

Returns the new Convex dataset_id so the caller can chain directly into :meth:upload_records (which resolves slug → id again on its own, but the id is useful when the caller already has it for logging / scripted-followups).

Use this once per slug as a setup step before the first :meth:upload_records. The agent-studio UI's "Create Dataset" dialog calls the Convex mutation directly without going through this HTTP route, so existing UI-created datasets need no migration.

Raises: ValueError: on 404 (route missing on an older backend), 409 (duplicate slug — message names the offending slug), or a malformed 2xx response missing dataset_id. RuntimeError: on 429 (rate limit) or any other server-side failure that _send doesn't already translate.

Args: slug: Unique short name used in URLs and CLI commands. Server enforces [a-zA-Z0-9._-]+ and 1-128 chars; bad slugs return 422. display_name: Human-readable label shown in the studio UI. Server enforces 1-256 chars; empty / whitespace returns 422. type_: One of "conversation", "qa_pair", "generic", "file_bundle". Determines which record-shape validation the upload path applies. For eval-results rows "generic" is the right choice. source: One of "upload", "curated", "disk", "mixed". Defaults to "upload" (the only CLI-relevant value). description: Optional human-readable description shown in UI. tags: Optional dataset-level tags (separate from per-version tags managed via :meth:tag_version).

Note: The Convex datasets:create mutation also accepts recordSchema (Pydantic-style per-field validators), but the HTTP route doesn't expose it yet and neither does this method. TODO: expose when there's a concrete caller.

StudioClient.download_conversations

download_conversations(mode: str | None = None, date_from: str | None = None, date_to: str | None = None, feedback: str | None = None, min_score: int | None = None, max_score: int | None = None, limit: int = 5000, annotations: bool = False) -> bytes

Download conversations as JSONL, following the export cursor.

The endpoint soft-limits each response and returns an X-Next-Cursor header while the stream isn't exhausted, so one selective export can span several requests. Follow the cursor until it's gone or limit rows have been collected, and return the concatenated JSONL bytes.

StudioClient.download_dataset

download_dataset(slug: str, version: int, fmt: str | None = None) -> httpx.Response

StudioClient.download_dataset_by_tag

download_dataset_by_tag(slug: str, tag: str, fmt: str | None = None) -> httpx.Response

Fetch the version of slug most-recently tagged tag.

Use this for the "comparison baseline" path — e.g. tag="production" always returns whatever version was last promoted. Pin to a specific version by passing version=... to :meth:download_dataset instead.

StudioClient.list_conversations

list_conversations(mode: str | None = None, date_from: str | None = None, date_to: str | None = None, feedback: str | None = None, min_score: int | None = None, max_score: int | None = None, limit: int = 50, cursor: str | None = None) -> ConversationListResponse

StudioClient.list_datasets

list_datasets(type: str | None = None) -> list[DatasetSummary]

StudioClient.resolve_dataset_id

resolve_dataset_id(slug: str) -> str

Look up the Convex _id of a dataset by slug.

Needed because the upload + publish routes are keyed on the internal ID, not the slug. Raises ValueError if the slug is unknown.

StudioClient.resolve_version

resolve_version(slug: str) -> int

StudioClient.tag_version

tag_version(slug: str, version: int, tag: str, exclusive: bool = False) -> None

Attach tag to a specific version of slug.

exclusive=True is the singleton-tag mode used for promotion labels like production: every other version of the same dataset loses the tag first. exclusive=False (the default) leaves other versions alone and is the right choice for multi-version labels like branch:feat-x.

StudioClient.untag_version

untag_version(slug: str, version: int, tag: str) -> None

Remove tag from a specific version. Idempotent.

The tag is URL-encoded into the path because tags can contain slashes (e.g. "branch/my-feature") or other URL-unsafe characters that would otherwise silently malform the request. tag_version doesn't need this — it sends tag in the JSON body, not the path.

StudioClient.upload_records

upload_records(slug: str, records: list[dict[str, Any]], published_by: str | None = None, filename: str = 'records.jsonl') -> int

Append records to slug as a new published version.

Two-step under the hood: upload to draft (validates + writes blob), then publish (promotes draft to version N+1). Returns the new version number on success.

Failure between the two steps leaves a dangling draft on the server — we deliberately don't try to clean it up because the most likely cause is a network or auth failure where the cleanup call would also fail. The draft is overwritten by the next successful upload, so callers don't need to take action.

Callers must ensure the dataset exists (use a one-time latent studio datasets create or the agent-studio UI). The dataset's type (generic/qa_pair/conversation) gates which record fields are validated; for eval-results rows generic is the right type.

Attributes

DatasetSource

DatasetType