liyaengine 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {liyaengine-0.2.0 → liyaengine-0.4.0}/PKG-INFO +64 -4
- liyaengine-0.4.0/README.md +168 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/pyproject.toml +1 -1
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/__init__.py +9 -1
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/client.py +4 -0
- liyaengine-0.4.0/src/liyaengine/resources/evaluations.py +465 -0
- liyaengine-0.4.0/src/liyaengine/resources/workflows.py +158 -0
- liyaengine-0.4.0/tests/test_evaluations.py +315 -0
- liyaengine-0.4.0/tests/test_workflows.py +163 -0
- liyaengine-0.2.0/README.md +0 -108
- {liyaengine-0.2.0 → liyaengine-0.4.0}/.github/workflows/ci.yml +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/.gitignore +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/LICENSE +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/_http.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/errors.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/py.typed +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/resources/__init__.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/resources/agents.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/src/liyaengine/resources/collections.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/tests/test_agents.py +0 -0
- {liyaengine-0.2.0 → liyaengine-0.4.0}/tests/test_collections.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: liyaengine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Official Python client for the Liya Engine public API
|
|
5
5
|
Project-URL: Homepage, https://liyaengine.ai
|
|
6
6
|
Project-URL: Documentation, https://liyaengine.ai/docs/sdks/python
|
|
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
Official Python client for the [Liya Engine](https://liyaengine.ai) public API.
|
|
33
33
|
|
|
34
|
-
> **Status: early access.** This SDK currently covers Collections and
|
|
34
|
+
> **Status: early access.** This SDK currently covers Collections, Agents, Workflows, and Evaluations. More resources (Domains, Run, Guardrail Policies) ship incrementally — see [Roadmap](#roadmap).
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
@@ -81,6 +81,66 @@ result = client.agents.run(agent.agent_key, input={"message": "My order hasn't a
|
|
|
81
81
|
history = client.agents.list_runs(agent.agent_key)
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Workflows
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
workflow = client.workflows.create(
|
|
88
|
+
name="Lead Intake",
|
|
89
|
+
steps=[{"step_type": "trigger", "config": {"trigger_subtype": "webhook"}}],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Workflows are created in draft status — deploy to publish and make them
|
|
93
|
+
# callable. Deploying a webhook-triggered workflow for the first time mints
|
|
94
|
+
# its webhook secret; capture it immediately, it is never returned again.
|
|
95
|
+
deployed = client.workflows.deploy(workflow.workflow_key)
|
|
96
|
+
webhook_url, webhook_secret = deployed["webhook_url"], deployed["webhook_secret"]
|
|
97
|
+
|
|
98
|
+
# Roll the secret with a grace window so in-flight senders don't break.
|
|
99
|
+
client.workflows.rotate_webhook_secret(workflow.workflow_key, grace_period_seconds=300)
|
|
100
|
+
|
|
101
|
+
# Flip a deployed workflow on/off without touching its definition.
|
|
102
|
+
client.workflows.toggle(workflow.workflow_key)
|
|
103
|
+
|
|
104
|
+
result = client.workflows.run(workflow.workflow_key, input={"email": "ada@example.com"})
|
|
105
|
+
|
|
106
|
+
history = client.workflows.list_runs(workflow.workflow_key)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> `deploy()` and `rotate_webhook_secret()` return the plaintext webhook secret exactly once. Store it immediately — subsequent reads (`get`, `list`) only ever expose `trigger_config["has_secret"]`.
|
|
110
|
+
|
|
111
|
+
## Evaluations
|
|
112
|
+
|
|
113
|
+
A Suite binds a Dataset to one specific intent; `suites.run()` calls that intent for real and scores what it produces. `runs.submit()`/`evaluations.score()` score a response you already generated yourself — no intent execution involved.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
dataset = client.evaluations.datasets.create(
|
|
117
|
+
name="Support Replies",
|
|
118
|
+
cases=[{"input": {"message": "Where is my order?"}}],
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
suite = client.evaluations.suites.create(
|
|
122
|
+
name="Order Status Suite",
|
|
123
|
+
domain_key="support",
|
|
124
|
+
intent_key="order-status",
|
|
125
|
+
dataset_id=dataset.id,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Runs are always async — poll for status/results.
|
|
129
|
+
run = client.evaluations.suites.run(suite.id)
|
|
130
|
+
result = client.evaluations.runs.get(run.id)
|
|
131
|
+
|
|
132
|
+
# A failed run can be resumed — already-scored cases are skipped.
|
|
133
|
+
client.evaluations.runs.resume(run.id)
|
|
134
|
+
|
|
135
|
+
# Score a response you already generated, no dataset/suite required.
|
|
136
|
+
scored = client.evaluations.score(
|
|
137
|
+
input={"message": "Where is my order?"},
|
|
138
|
+
output="Your order shipped yesterday and should arrive by Friday.",
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> `custom_scorer_webhook_secret` on a Suite is write-only — it's never returned; only a `custom_scorer_webhook_secret_set` boolean comes back on reads.
|
|
143
|
+
|
|
84
144
|
## Error handling
|
|
85
145
|
|
|
86
146
|
Every failed request raises `LiyaEngineAPIError`, carrying the API's `code`, `message`, and HTTP `status`:
|
|
@@ -114,11 +174,11 @@ LiyaEngine(
|
|
|
114
174
|
|
|
115
175
|
- [x] Collections
|
|
116
176
|
- [x] Agents (full CRUD, deploy, run, run/session history)
|
|
177
|
+
- [x] Workflows (full CRUD, toggle, deploy, webhook secret rotate, run, run history)
|
|
178
|
+
- [x] Evaluations (Datasets/Cases/Suites/Runs/Reviews CRUD, suite execution, cancel/resume, statistical + pairwise compare, standalone scoring)
|
|
117
179
|
- [ ] Domains (custom domain + intent CRUD)
|
|
118
180
|
- [ ] Run / Run (streaming)
|
|
119
|
-
- [ ] Workflows
|
|
120
181
|
- [ ] Guardrail Policies
|
|
121
|
-
- [ ] Evaluations
|
|
122
182
|
- [ ] Async client
|
|
123
183
|
|
|
124
184
|
Full docs: https://liyaengine.ai/docs/sdks/python
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# liyaengine
|
|
2
|
+
|
|
3
|
+
Official Python client for the [Liya Engine](https://liyaengine.ai) public API.
|
|
4
|
+
|
|
5
|
+
> **Status: early access.** This SDK currently covers Collections, Agents, Workflows, and Evaluations. More resources (Domains, Run, Guardrail Policies) ship incrementally — see [Roadmap](#roadmap).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install liyaengine
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from liyaengine import LiyaEngine
|
|
17
|
+
|
|
18
|
+
client = LiyaEngine(api_key="liya_...")
|
|
19
|
+
|
|
20
|
+
collection = client.collections.create(
|
|
21
|
+
slug="contracts",
|
|
22
|
+
label="Contracts",
|
|
23
|
+
domain_keys=["legal-ops"],
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
collections = client.collections.list()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or as a context manager (closes the underlying HTTP connection pool automatically):
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
with LiyaEngine(api_key="liya_...") as client:
|
|
33
|
+
collections = client.collections.list()
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Get an API key from your [Liya Engine dashboard](https://app.liyaengine.ai) under Settings → API Keys.
|
|
37
|
+
|
|
38
|
+
## Agents
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
agent = client.agents.create(
|
|
42
|
+
agent_key="support-triage",
|
|
43
|
+
name="Support Triage",
|
|
44
|
+
goal="Triage incoming support tickets and route them to the right team.",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Agents are created in draft status — deploy to activate for execution.
|
|
48
|
+
client.agents.deploy(agent.agent_key)
|
|
49
|
+
|
|
50
|
+
result = client.agents.run(agent.agent_key, input={"message": "My order hasn't arrived yet."})
|
|
51
|
+
|
|
52
|
+
history = client.agents.list_runs(agent.agent_key)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Workflows
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
workflow = client.workflows.create(
|
|
59
|
+
name="Lead Intake",
|
|
60
|
+
steps=[{"step_type": "trigger", "config": {"trigger_subtype": "webhook"}}],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Workflows are created in draft status — deploy to publish and make them
|
|
64
|
+
# callable. Deploying a webhook-triggered workflow for the first time mints
|
|
65
|
+
# its webhook secret; capture it immediately, it is never returned again.
|
|
66
|
+
deployed = client.workflows.deploy(workflow.workflow_key)
|
|
67
|
+
webhook_url, webhook_secret = deployed["webhook_url"], deployed["webhook_secret"]
|
|
68
|
+
|
|
69
|
+
# Roll the secret with a grace window so in-flight senders don't break.
|
|
70
|
+
client.workflows.rotate_webhook_secret(workflow.workflow_key, grace_period_seconds=300)
|
|
71
|
+
|
|
72
|
+
# Flip a deployed workflow on/off without touching its definition.
|
|
73
|
+
client.workflows.toggle(workflow.workflow_key)
|
|
74
|
+
|
|
75
|
+
result = client.workflows.run(workflow.workflow_key, input={"email": "ada@example.com"})
|
|
76
|
+
|
|
77
|
+
history = client.workflows.list_runs(workflow.workflow_key)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> `deploy()` and `rotate_webhook_secret()` return the plaintext webhook secret exactly once. Store it immediately — subsequent reads (`get`, `list`) only ever expose `trigger_config["has_secret"]`.
|
|
81
|
+
|
|
82
|
+
## Evaluations
|
|
83
|
+
|
|
84
|
+
A Suite binds a Dataset to one specific intent; `suites.run()` calls that intent for real and scores what it produces. `runs.submit()`/`evaluations.score()` score a response you already generated yourself — no intent execution involved.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
dataset = client.evaluations.datasets.create(
|
|
88
|
+
name="Support Replies",
|
|
89
|
+
cases=[{"input": {"message": "Where is my order?"}}],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
suite = client.evaluations.suites.create(
|
|
93
|
+
name="Order Status Suite",
|
|
94
|
+
domain_key="support",
|
|
95
|
+
intent_key="order-status",
|
|
96
|
+
dataset_id=dataset.id,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Runs are always async — poll for status/results.
|
|
100
|
+
run = client.evaluations.suites.run(suite.id)
|
|
101
|
+
result = client.evaluations.runs.get(run.id)
|
|
102
|
+
|
|
103
|
+
# A failed run can be resumed — already-scored cases are skipped.
|
|
104
|
+
client.evaluations.runs.resume(run.id)
|
|
105
|
+
|
|
106
|
+
# Score a response you already generated, no dataset/suite required.
|
|
107
|
+
scored = client.evaluations.score(
|
|
108
|
+
input={"message": "Where is my order?"},
|
|
109
|
+
output="Your order shipped yesterday and should arrive by Friday.",
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> `custom_scorer_webhook_secret` on a Suite is write-only — it's never returned; only a `custom_scorer_webhook_secret_set` boolean comes back on reads.
|
|
114
|
+
|
|
115
|
+
## Error handling
|
|
116
|
+
|
|
117
|
+
Every failed request raises `LiyaEngineAPIError`, carrying the API's `code`, `message`, and HTTP `status`:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from liyaengine import LiyaEngineAPIError
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
client.collections.create(slug="contracts", label="Contracts", domain_keys=["legal-ops"])
|
|
124
|
+
except LiyaEngineAPIError as err:
|
|
125
|
+
if err.code == "SLUG_CONFLICT":
|
|
126
|
+
# handle the conflict
|
|
127
|
+
pass
|
|
128
|
+
raise
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Network failures and timeouts raise `LiyaEngineNetworkError` instead. Requests are retried automatically on `429`/`5xx` responses and transient network errors (2 retries by default).
|
|
132
|
+
|
|
133
|
+
## Configuration
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
LiyaEngine(
|
|
137
|
+
api_key="liya_...",
|
|
138
|
+
base_url="https://api.liyaengine.ai", # override for local/staging
|
|
139
|
+
timeout_s=30.0,
|
|
140
|
+
max_retries=2,
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Roadmap
|
|
145
|
+
|
|
146
|
+
- [x] Collections
|
|
147
|
+
- [x] Agents (full CRUD, deploy, run, run/session history)
|
|
148
|
+
- [x] Workflows (full CRUD, toggle, deploy, webhook secret rotate, run, run history)
|
|
149
|
+
- [x] Evaluations (Datasets/Cases/Suites/Runs/Reviews CRUD, suite execution, cancel/resume, statistical + pairwise compare, standalone scoring)
|
|
150
|
+
- [ ] Domains (custom domain + intent CRUD)
|
|
151
|
+
- [ ] Run / Run (streaming)
|
|
152
|
+
- [ ] Guardrail Policies
|
|
153
|
+
- [ ] Async client
|
|
154
|
+
|
|
155
|
+
Full docs: https://liyaengine.ai/docs/sdks/python
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
161
|
+
pip install -e ".[dev]"
|
|
162
|
+
mypy src
|
|
163
|
+
pytest
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
@@ -2,6 +2,8 @@ from .client import LiyaEngine
|
|
|
2
2
|
from .errors import LiyaEngineAPIError, LiyaEngineNetworkError
|
|
3
3
|
from .resources.agents import Agent
|
|
4
4
|
from .resources.collections import Collection
|
|
5
|
+
from .resources.workflows import Workflow
|
|
6
|
+
from .resources.evaluations import EvalDataset, EvalCase, EvalSuite, EvalRun, EvalReview
|
|
5
7
|
|
|
6
8
|
__all__ = [
|
|
7
9
|
"LiyaEngine",
|
|
@@ -9,6 +11,12 @@ __all__ = [
|
|
|
9
11
|
"LiyaEngineNetworkError",
|
|
10
12
|
"Collection",
|
|
11
13
|
"Agent",
|
|
14
|
+
"Workflow",
|
|
15
|
+
"EvalDataset",
|
|
16
|
+
"EvalCase",
|
|
17
|
+
"EvalSuite",
|
|
18
|
+
"EvalRun",
|
|
19
|
+
"EvalReview",
|
|
12
20
|
]
|
|
13
21
|
|
|
14
|
-
__version__ = "0.
|
|
22
|
+
__version__ = "0.4.0"
|
|
@@ -7,6 +7,8 @@ import httpx
|
|
|
7
7
|
from ._http import HttpClient
|
|
8
8
|
from .resources.agents import AgentsResource
|
|
9
9
|
from .resources.collections import CollectionsResource
|
|
10
|
+
from .resources.workflows import WorkflowsResource
|
|
11
|
+
from .resources.evaluations import EvaluationsResource
|
|
10
12
|
|
|
11
13
|
_DEFAULT_BASE_URL = "https://api.liyaengine.ai"
|
|
12
14
|
|
|
@@ -41,6 +43,8 @@ class LiyaEngine:
|
|
|
41
43
|
)
|
|
42
44
|
self.collections = CollectionsResource(self._http)
|
|
43
45
|
self.agents = AgentsResource(self._http)
|
|
46
|
+
self.workflows = WorkflowsResource(self._http)
|
|
47
|
+
self.evaluations = EvaluationsResource(self._http)
|
|
44
48
|
|
|
45
49
|
def close(self) -> None:
|
|
46
50
|
self._http.close()
|