Skip to content

kindgi.client

View as Markdown

The Kindgi API client.

from kindgi.client import Kindgi
client = Kindgi() # KINDGI_API_URL, KINDGI_API_TOKEN; in development, the running kindgi dev
run = client.runs.start(flow="acme.ledger.record-flow", input={"vendor": "Acme"})
print(run.status, run.output)

Every operation of the API (@kindgi/api/openapi.json) is a method: the operation id approvals.reviewers.list is client.approvals.reviewers.list(). Request bodies are a model from kindgi.client.models, a mapping, or their fields as keywords; answers are models. Errors are typed (NotFoundError, GuardrailViolationError, …). AsyncKindgi is the same with asyncio.

Without arguments, a client takes KINDGI_API_URL and KINDGI_API_TOKEN from the environment. In development, when they're unset, it uses the running kindgi dev (the nearest .kindgirc.json), and warns once (KindgiConfigWarning) to put them in your env file (.env / .env.local). Production (KINDGI_ENV or NODE_ENV set to production) never reads .kindgirc.json.

class AsyncKindgi(
base_url: str | None = None,
*,
token: str | None = None,
timeout: float = 60.0,
max_retries: int = 2,
default_headers: Mapping[str, str] | None = None,
http_client: httpx.AsyncClient | None = None,
)
Signature

The Kindgi API, with asyncio.

async with AsyncKindgi() as client: run = await client.runs.start(flow="acme.ledger.record-flow", input={...}) async for event in client.runs.follow(run.id): # to the run's end ...

Like Kindgi, it finds its URL and token on first use, not when it's created.

class AuthError(
message: str,
*,
reason: Literal['unauthenticated', 'forbidden', 'token-expired'],
**kw: Any,
)
Signature
class ConflictError(
message: str,
*,
status: int | None = None,
server_code: str | None = None,
details: Mapping[str, Any] | None = None,
request_id: str | None = None,
)
Signature
class GuardrailViolationError(
message: str,
*,
violations: list[Mapping[str, Any]],
evaluation_errors: list[Mapping[str, Any]],
**kw: Any,
)
Signature
class InvalidRequestError(message: str, *, issues: list[Mapping[str, Any]], **kw: Any)
Signature
class Kindgi(
base_url: str | None = None,
*,
token: str | None = None,
timeout: float = 60.0,
max_retries: int = 2,
default_headers: Mapping[str, str] | None = None,
http_client: httpx.Client | None = None,
)
Signature

The Kindgi API, synchronously.

client = Kindgi("http://127.0.0.1:4000", token="kgi_bt_…")
client = Kindgi() # KINDGI_API_URL, KINDGI_API_TOKEN; in development, kindgi dev
run = client.runs.start(agent="acme.bookkeeper", input={"userMessage": "hi"})
Excerpt

Resources follow the API's operation ids (client.approvals.reviewers.list()). Use it as a context manager, or close() it, to release connections.

The URL and token are found when the client is first used (its first request, or base_url / token), not when it's created: a module-scope Kindgi() loads in a build step that runs without them. If they're still missing then, that use raises ValueError, naming what to set.

class KindgiApiError(
message: str,
*,
status: int | None = None,
server_code: str | None = None,
details: Mapping[str, Any] | None = None,
request_id: str | None = None,
)
Signature

A Kindgi API call failed.

class KindgiConfigWarning(*args, **kwargs)
Signature

How the client found its runtime needs your attention (a fallback, a stale token).

class NetworkError(
message: str,
*,
timeout: float | None = None,
status: int | None = None,
)
Signature

The request didn't get an answer (connection, timeout, a body that isn't JSON).

class NotFoundError(message: str, *, kind: str, id: str, **kw: Any)
Signature
class RateLimitedError(
message: str,
*,
retry_after_seconds: float | None = None,
**kw: Any,
)
Signature
class ServerError(
message: str,
*,
status: int | None = None,
server_code: str | None = None,
details: Mapping[str, Any] | None = None,
request_id: str | None = None,
)
Signature
apaginate(
list_method: Callable[..., Awaitable[_Page[T]]],
/,
*args: Any,
**kwargs: Any,
) -> AsyncIterator[T]
Signature

paginate for AsyncKindgi: async for run in apaginate(client.runs.list): ….

comparison_of(run: EvalRun) -> JudgedComparisonResult | None
Signature

A comparison's result, typed: its summary and each case (per_case).

run is a judged eval run (a test set compared with a version). None for another kind of eval run, a dry run, or one that hasn't finished. The same reading as comparisonOf in the TypeScript client.

paginate(
list_method: Callable[..., _Page[T]],
/,
*args: Any,
**kwargs: Any,
) -> Iterator[T]
Signature

Every item of a list operation, page after page: paginate(client.runs.list, limit=50).

pins_digest(pins: Mapping[str, Any] | BaseModel) -> str
Signature

sha256:<hex> of an agent version's pins, the string in its pins_digest.

pins is an agent version's pins (a model or a mapping with tools, prompts and settings). Two agent versions with the same digest run the same blocks. The same string as pinsDigest in @kindgi/agents.