Skip to main content

Liyagent Python SDK

PyPI version Python versions License

Build, run, evaluate and govern AI agents on Liyagent from Python. Install an agent from a template, ask it a question, gate a release on its eval score and work its approval queue, all with the same permissions, guardrails and audit trail as the console.

  • Blocking Client and asyncio AsyncClient, with the same calls
  • Typed request bodies (py.typed), a typed exception per error kind
  • Retries with backoff on 429 and 5xx (respects Retry-After), timeouts
  • One dependency: httpx

Install

pip install liyagent

Python 3.10 or later.

Quickstart

1. Get a token. In the Liyagent console, open Access → Service accounts → + New service account, choose the account it acts as and copy the secret (it is shown once). The token is svc/<name>:<secret>. The service account can do only what that account's role allows. Installing a template, as below, needs a role that writes agents and guardrails, such as Administrator. See Service accounts.

2. Set the address and the token. The address is the URL you open the console at: https://liyagent.com for the hosted service, or your own host if you run Liyagent yourself.

export LIYA_URL="https://liyagent.com"
export LIYA_TOKEN="svc/ci-deployer:..."

3. Run it.

from liyagent import Client

with Client() as liya:                      # reads LIYA_URL and LIYA_TOKEN
    for t in liya.templates.list(solution="itsm"):
        print(t["key"], t["version"], t.get("title", ""))

    plan = liya.templates.preview("it-ticket-triage", agent_id="desk-triage")
    if plan["valid"]:
        liya.templates.install("it-ticket-triage", agent_id="desk-triage")
    else:
        print("would be refused at", plan["step"], plan["refusal"]["detail"])

    reply = liya.agents.ask("desk-triage", "VPN drops every 10 minutes since this morning")
    print(reply["reply"] or reply.get("skipped"))

You can also pass everything explicitly: Client("https://liyagent.com", token="svc/ci-deployer:...").

Authentication

Credential Use it for How
Service account svc/<name>:<secret> CI, scripts, services (recommended) token= or LIYA_TOKEN. Exchanged for a one-hour session, renewed automatically.
Session token A token another step minted token= (sent as it is, not renewed)
Username and password Local experiments username=, password=. Accounts that require an authenticator code can't sign in this way; use a service account.
Instance API key or client token knowledge.answer only api_key= or LIYA_API_KEY

Core concepts

Every namespace maps to one part of the platform. Calls return the server's JSON (dict/list) as it is. The API endpoints page documents every field.

Agents

for agent in liya.agents.list(filter="enabled = true"):   # follows every page
    print(agent["id"], agent["label"])

liya.agents.put("desk-helper", label="Desk helper", tags=["team=it"])  # create or change
answer = liya.agents.ask("desk-helper", "How do I reset my password?")

bundle = liya.agents.export("desk-helper")            # agents as code
liya.agents.apply(bundle, dry_run=True)               # plan without writing

Runs

# A workflow runs to its end, or to its first wait (an approval).
run = liya.agents.run_workflow("desk-triage", "triage", {"title": "Laptop won't boot"})
print(run["run_id"], run["status"], run.get("waiting_on") or run.get("error"))
run = liya.agents.workflow_run("desk-triage", run["run_id"])     # poll it later

recent = liya.runs.list(limit=20)                     # agent runs, newest first
# A run suspended for input resumes with the answer:
# liya.runs.resume("<run id>", answer="yes")

Approvals

Risky steps wait for a person. Approve by digest so an edit made since you read it refuses the decision:

for item in liya.approvals.list(status="pending"):
    liya.approvals.approve(item["id"], reason="checked", digest=item.get("digest", ""))

Evals

started = liya.evals.start_run("desk-triage-evals", "desk-triage")   # a background job
result = liya.evals.wait(started["run"]["id"], timeout=900)
print(result["status"], result["metrics"])

To gate a release, pin a good run as the baseline in the console, then pass baseline="pinned", allowed_drop=0.05. result["passed"] is then False when the agent scores worse than the baseline by more than the allowed drop.

Knowledge

hits = Client(api_key="...").knowledge.answer("How do I connect to the VPN?", top_k=3)
for article in liya.knowledge.articles(status="draft")["articles"]:
    liya.knowledge.approve_article(article["id"])

Templates

A template installs an agent with its guardrails, workflow, triggers, eval set and budget in one step. preview makes every write and rolls it back, so you see the plan and any refusal first.

liya.templates.install("hr-policy-answers", agent_id="people-policy",
                       params={"team": "People Ops"})
liya.templates.uninstall("people-policy", reason="replaced", dry_run=True)

To publish your own templates, use draft_from_agent, validate, save_draft, publish and withdraw. These need templates.write.

Anything else

liya.request("GET", "/api/...", params={...}, json={...}) calls any route with the same credentials, retries and errors. liya.check_server() lists the operations this SDK uses that an older server does not serve.

Errors

Every failure raises a subclass of liyagent.LiyagentError:

Exception When kind
AuthenticationError 401: no credentials, or they were refused auth
PermissionDeniedError 403: the role lacks a permission (the message names it) forbidden
NotFoundError 404 not_found
BadRequestError 400, 422: the body does not match the schema invalid
ConflictError 409, 412: exists already, or changed since read conflict
RateLimitError 429, after the retries rate_limited
InternalServerError 5xx, after the retries server
APIConnectionError the server was not reached or timed out unreachable

Each one carries status, detail (the server's reason; for a bundle or template refusal, {message, at} pointing at the part of the document), request_id (quote it to support), method and path.

from liyagent import Client, NotFoundError, PermissionDeniedError

try:
    liya.agents.get("no-such-agent")
except NotFoundError as e:
    print(e.status, e.detail, e.request_id)
except PermissionDeniedError as e:
    print("ask an admin for:", e.detail)

Retries and timeouts

A 429 or 503 is sent again for any method, because the server did not run it. So is a request that never reached the server. A 500, 502 or 504, or a read timeout, is retried only for GET, PUT and DELETE, which are safe to repeat. The client waits as long as Retry-After says, or otherwise backs off exponentially with jitter (0.5 s, 1 s, 2 s, up to 8 s).

liya = Client(max_retries=5, timeout=30.0)   # defaults: 2 retries, 60 s
liya = Client(max_retries=0)                 # no retries

Set logging.getLogger("liyagent") to INFO to see each retry.

Async

import asyncio
from liyagent import AsyncClient

async def main() -> None:
    async with AsyncClient() as liya:
        async for agent in liya.agents.list():
            print(agent["id"], agent["label"])
        templates, runs = await asyncio.gather(liya.templates.list(), liya.runs.list())

asyncio.run(main())

Types

The package ships py.typed. Request bodies are TypedDicts in liyagent.models (TemplateInstallBody, BundleApplyBody, EvalOfflineRunBody, ...). They are generated from the server's OpenAPI document, so mypy and pyright check what you send.

Licence

Apache-2.0

Metadata

Release files for liyagent 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for liyagent 0.2.1
File Size Uploaded Provenance Actions
liyagent-0.2.1.tar.gz 31.5 kB Without provenance Details for liyagent-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for liyagent 0.2.1
File Interpreter ABI Platform Actions
liyagent-0.2.1-py3-none-any.whl Python 3 none any Details for liyagent-0.2.1-py3-none-any.whl

Total release size: 59.9 kB

Release files / liyagent-0.2.1.tar.gz

Download URL liyagent-0.2.1.tar.gz
Size 31.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a450b2b557037a96750eeeeffba7cf1d153978c87d3cf24949c972e988e31179
BLAKE2b-256 checksum
How to use checksums
131949336dc18125c5301bb47d5328b3123434aab1c953f2a786e9d1ebdd1583
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / liyagent-0.2.1-py3-none-any.whl

Download URL liyagent-0.2.1-py3-none-any.whl
Size 28.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
58eee2a098792f5be4db42b1dc43b4a90ed4448dcdf602a5b14c59562fc03b08
BLAKE2b-256 checksum
How to use checksums
bedb02b392fb195ccc294a6ab408084a998edb9d02a95ea820a7caa43b838183
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Security for liyagent 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page