Official async Python client for the Cominty managed agent chat API
Project description
Cominty Python SDK
Official async Python client for the Cominty managed agent chat API.
Start a conversation with an agent, stream its progress live, and manage threads — with a small, fully-typed surface that's the same on Python 3.9 through 3.13.
import asyncio
from cominty_sdk import AsyncCominty
async def main() -> None:
async with AsyncCominty() as client: # reads COMINTY_API_KEY + COMINTY_USER_ID
run = await client.chat.start(agent_id="__cominty_agents::agent.chat",
message="What is Cominty?")
print(await run.text())
asyncio.run(main())
- Async-first, built on
httpx. - Fully typed — ships
py.typed; strict-checked with pyright. Pydantic models everywhere. - One handle for streaming and awaiting — iterate a run for live progress
events, or just
await run.text()for the final answer. - Fail-fast validation — bad parameters raise locally, before any request.
- Typed errors — every failure is a
ComintyErrorsubclass.
Requirements
- Python 3.9+
- A Cominty API key and your user id (see Authentication)
Installation
pip install cominty-sdk
# or
uv add cominty-sdk
Authentication
You need two things, both from platform.cominty.ai:
- API key → platform.cominty.ai/api-keys (shown once — copy it).
- Your user id → avatar (top right) → Profile. It looks like
user_31HPTBuBvX20xlQNAbvxjOxPbKB.
The user id identifies the end user every request acts on behalf of. It's set
once on the client (or via COMINTY_USER_ID) and applied to every call.
The simplest setup is environment variables:
export COMINTY_API_KEY="<your API key>"
export COMINTY_USER_ID="user_..."
async with AsyncCominty() as client: # picks both up from the environment
...
…or pass them explicitly (explicit arguments win over the environment):
client = AsyncCominty(api_token="<your API key>", user_id="user_...")
A malformed user_id is rejected at construction, not as a server error later.
Picking an agent
Every chat call takes an agent_id. Browse your agents and copy an id at
platform.cominty.ai/agents — they look
like __cominty_agents::agent.chat.
Quick start
Every conversation starts with chat.start, which returns a run — a handle
to the assistant's in-progress reply. From there, pick the style you need.
Just get the answer
run = await client.chat.start(agent_id=AGENT_ID, message="Give me one fun fact.")
print(await run.text()) # blocks until the agent finishes
await run.result() gives the full Message (status, files, structured output,
questions). text() is shorthand for result().content.
Stream progress events
Iterating a run yields progress events only — tool calls, LLM steps, the result event — as they happen. The finished reply is captured for you.
from cominty_sdk import events
run = await client.chat.start(agent_id=AGENT_ID, message="Research X and summarize.")
async for event in run:
if isinstance(event, events.ToolCall):
print(f"tool {event.data.name} -> {event.status}")
elif isinstance(event, events.LlmStep):
print(f"llm {event.data.description}")
elif isinstance(event, events.Result):
print(f"cost {event.data.cost.total}")
print("FINAL:", await run.text()) # available after the stream drains
A run's stream is single-use: iterate it or await its result — the result is cached, so calling
text()/result()after iterating is free.
Continue the conversation
chat.send(thread_id, ...) is the mirror of start for an existing thread:
same arguments, same streamable run. The agent keeps the thread's context.
first = await client.chat.start(agent_id=AGENT_ID, message="Pick a language.")
await first.text()
second = await client.chat.send(
first.thread.id, agent_id=AGENT_ID, message="Now show hello-world in it.",
)
print(await second.text())
Answer the agent's questions
When an agent needs more input, it ends its turn with clarifying questions
(a prompt plus suggested options) instead of a final answer. Read them, then
answer with a normal follow-up:
run = await client.chat.start(agent_id=AGENT_ID, message="Book me a room.")
await run.text()
for q in await run.questions():
print(q.prompt, q.options)
# answer = the chosen option (or free text)
reply = await client.chat.send(run.thread.id, agent_id=AGENT_ID, message="Tomorrow 10am")
print(await reply.text())
Manage threads
client.threads is scoped to the client's user_id automatically.
# List & search the user's conversations (summaries — no messages)
for t in await client.threads.list(limit=20):
print(t.created_at, t.name, t.id)
await client.threads.list(terms=["invoice"]) # free-text search
await client.threads.list(limit=10, page=1) # paginate (zero-based)
# Load one thread's full history
thread = await client.threads.get(thread_id)
print(len(thread.messages))
# Partial update — only the fields you pass change (returns a ThreadSummary)
await client.threads.update(thread_id, name="Renamed", starred=True)
# Archive (soft-delete)
await client.threads.archive(thread_id)
Examples
Runnable scripts for each scenario live in examples/:
| Script | Shows |
|---|---|
01_stream_events.py |
Stream progress events live |
02_await_result.py |
Fire and await the final answer |
03_follow_up.py |
Continue in the same thread |
04_answer_questions.py |
Read & answer agent questions |
05_list_threads.py |
List and search threads |
06_manage_thread.py |
Get, rename/star, archive |
07_custom_agent.py |
Call a custom managed agent (your own model + instructions) |
08_mcp_linear.py |
Custom agent pulls live context from an MCP server (Linear) |
They render colored, aligned output with rich,
which ships in the dev extras:
uv sync --all-extras --dev # installs rich (or: pip install rich)
export COMINTY_API_KEY=... COMINTY_USER_ID=user_...
python examples/01_stream_events.py
Message parameters
Both chat.start and chat.send accept:
| Argument | Type | Notes |
|---|---|---|
agent_id |
str |
Required. The agent to run. |
message |
str |
Required. The user's message (max 30,000 chars). |
name |
str |
start only — names the new thread. |
file_ids |
list[str] |
Attach previously-uploaded files (max 5). |
source_ids |
list[int] |
Restrict retrieval to specific knowledge sources. |
document_ids |
list[str] |
Restrict retrieval to specific documents. |
disabled_tools |
list[str] |
Turn tools off: "web", "company_documents", "mcp:<server>", or "mcp:*" for all MCP. |
Invalid values raise InvalidParams before any request is sent.
Configuration
| Argument | Env var | Default |
|---|---|---|
api_token |
COMINTY_API_KEY |
— (required) |
user_id |
COMINTY_USER_ID |
— (required) |
base_url |
COMINTY_BASE_URL |
https://ds.cominty.com |
timeout |
— | 60 (seconds) |
Resolution order for each option: explicit argument → environment variable →
default. The SDK does not auto-load .env; export the vars or load the
file yourself (see .env.example).
Error handling
Every error is a subclass of ComintyError:
from cominty_sdk import (
ComintyError, # base — catch-all
APIError, # any 4xx/5xx; carries .status_code and a typed .error body
AuthError, # 401
PermissionError, # 403
NotFoundError, # 404
ConflictError, # 409
RateLimitError, # 429 — exposes .reset_at
ServerError, # 5xx
APIConnectionError, # network failure / timeout, no response
StreamInterrupted, # server shut down mid-stream — carries the .partial Message
InvalidParams, # client-side validation failed — .errors lists each problem
SDKError, # unexpected SDK-internal condition
)
try:
run = await client.chat.start(agent_id=AGENT_ID, message="hi")
print(await run.text())
except RateLimitError as e:
print(f"slow down — retry after {e.reset_at}")
except APIError as e:
print(f"API error {e.status_code}: {e.error}")
Development
uv sync --all-extras --dev
uv run pytest # tests
uv run ruff check . # lint
uv run pyright # type-check (strict)
Integration tests are opt-in (they hit the real API):
COMINTY_API_KEY=... COMINTY_USER_ID=... uv run pytest -m integration
See AGENTS.md for coding conventions (typing, versioning, models).
Releasing
Publishing to PyPI uses Trusted Publishing (OIDC) — no tokens stored in
GitHub — and is triggered by publishing a GitHub Release
(.github/workflows/release.yml). The published version comes from
pyproject.toml, so the tag is cosmetic; keep them in sync.
# 1. bump the version in BOTH pyproject.toml and src/cominty_sdk/_version.py
# 2. commit on main and push
# 3. create the release — this tags and triggers the publish
gh release create v0.4.0 --title "v0.4.0" --generate-notes
# pre-release rehearsal: gh release create v0.4.0rc1 --prerelease --generate-notes
A local rehearsal to TestPyPI is available via uv run invoke publish-test.
License
MIT
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
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 cominty_sdk-0.4.0.tar.gz.
File metadata
- Download URL: cominty_sdk-0.4.0.tar.gz
- Upload date:
- Size: 20.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83b4ded61dfb1627e77d67259b56822e05bc042cc6bfc2621baa364d3e45905f
|
|
| MD5 |
ddc2b58036ca131b59d4b980eea565a3
|
|
| BLAKE2b-256 |
a33d1aa43d5d041103b3d4f523e54d83f292e8ab41bfa41c3d40dd18164797da
|
Provenance
The following attestation bundles were made for cominty_sdk-0.4.0.tar.gz:
Publisher:
release.yml on cominty/python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cominty_sdk-0.4.0.tar.gz -
Subject digest:
83b4ded61dfb1627e77d67259b56822e05bc042cc6bfc2621baa364d3e45905f - Sigstore transparency entry: 2015440083
- Sigstore integration time:
-
Permalink:
cominty/python-sdk@7cc288925c99acd9635422ecec21e0588daed504 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/cominty
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7cc288925c99acd9635422ecec21e0588daed504 -
Trigger Event:
release
-
Statement type:
File details
Details for the file cominty_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: cominty_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ee0a5baac3ab7c536055948a33f054fc12c6cf29c9eb6f488456a5197787bcc
|
|
| MD5 |
0f67eefa8d8d0ff0dcd3a25dbf0ec39f
|
|
| BLAKE2b-256 |
c6d7a687f975bbf2f2dac0ad94ae401e0acd7d79e270e70a3a984bd05718ae0f
|
Provenance
The following attestation bundles were made for cominty_sdk-0.4.0-py3-none-any.whl:
Publisher:
release.yml on cominty/python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cominty_sdk-0.4.0-py3-none-any.whl -
Subject digest:
0ee0a5baac3ab7c536055948a33f054fc12c6cf29c9eb6f488456a5197787bcc - Sigstore transparency entry: 2015440157
- Sigstore integration time:
-
Permalink:
cominty/python-sdk@7cc288925c99acd9635422ecec21e0588daed504 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/cominty
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7cc288925c99acd9635422ecec21e0588daed504 -
Trigger Event:
release
-
Statement type: