Skip to content

Commit 6524e8f

Browse files
author
Convilyn Release
committed
Release v2.2.0b1
Generated public mirror snapshot.
1 parent 23c1292 commit 6524e8f

10 files changed

Lines changed: 129 additions & 11 deletions

File tree

‎.secrets.baseline‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎CHANGELOG.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,46 @@ versioning follows [Semantic Versioning](https://semver.org/).
55

66
## [Unreleased]
77

8+
### Docs
9+
10+
- **QUICKSTART now starts at the actual step 0**: minting a `cvl_` developer
11+
key via `ConvilynClient().register(...)` (the consumer `ck_` key does not
12+
work on the author track, and there is no console UI for developer
13+
registration yet). Also documents the server-less submission path for
14+
authors who cannot host a public endpoint.
15+
- **Hosted-runtime error status corrected: 503, not 501.** Current platform
16+
builds answer `503 HOSTED_NOT_AVAILABLE` (older builds used 501;
17+
router-unmounted environments answer 404). `deploy --hosted`'s CLI hint now
18+
matches both codes (TS parity), and the `deploy_hosted_runtime` docstring
19+
and DEPLOYMENT.md no longer claim 501.
20+
21+
## [2.2.0b1] — 2026-07-11
22+
23+
### Added
24+
25+
- **Server-less workflow submission.** `submit_workflow(workflow_spec)` no
26+
longer requires `server_ids` — omit it (or pass an empty sequence) when
27+
your spec orchestrates only platform built-in tools (OCR / document
28+
parsing / analysis), the same shape every built-in goal-lane workflow
29+
uses. No self-hosted HMAC tool server is needed for that class of
30+
workflow, which unblocks edge/NAT authors who cannot expose a public
31+
endpoint. The platform still enforces the `dev_{prefix}.` spec namespace
32+
and warns on tool references it cannot resolve.
33+
34+
## [2.1.1b5] — 2026-07-10
35+
36+
### Fixed
37+
38+
- **`WorkflowSpec` name/tool-count bounds are now actually enforced.**
39+
2.1.1b4 added the check to the standalone `validate_workflow_spec()`
40+
pre-submission validator, but `WorkflowSpec.compile()` (the path every
41+
author and the community re-test actually exercises) never calls that
42+
function, so a 200-character name or 30 tool references still compiled
43+
successfully. The bound is now a `pydantic.Field` constraint on
44+
`WorkflowBlueprint.name` / `MCPConfigModel.tools`, enforced immediately
45+
at `use_tools()` / `compile()` time — the same place the TypeScript
46+
author SDK enforces it.
47+
848
## [2.1.1b4] — 2026-07-10
949

1050
### Added

‎docs/DEPLOYMENT.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ to self-host:
247247
convilyn-author deploy --hosted --region us-east-1 # preview
248248
```
249249

250-
This currently returns `501 HOSTED_NOT_AVAILABLE` in most accounts. The
250+
This currently returns `HOSTED_NOT_AVAILABLE` in most accounts (HTTP
251+
503 on current platform builds; older builds answered 501, and
252+
environments with the author-runtime router unmounted answer 404). The
251253
self-hosted targets above (Lambda / Fargate / VM), registered with
252-
`convilyn-author push`, are the supported paths for the beta.
254+
`convilyn-author push`, are the supported paths for the beta. If you
255+
cannot expose a public endpoint at all (edge device behind NAT) and
256+
your workflow only needs platform built-in tools, submit it server-less
257+
instead — see `submit_workflow` (no `server_ids`, no HMAC endpoint).

‎docs/README.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,46 @@ Build tool servers + workflow specs for the Convilyn AI workflow platform.
1616
1717
## Quick Start
1818

19+
### Step 0 — get a developer key (`cvl_`)
20+
21+
The author track authenticates with a **developer** key (`cvl_` prefix) —
22+
your consumer key (`ck_`, from the web console) will NOT work here. Mint
23+
one by registering as a developer; there is no console UI for this yet:
24+
25+
```python
26+
import asyncio
27+
from convilyn_sdk import ConvilynClient
28+
29+
async def main() -> None:
30+
result = await ConvilynClient().register(
31+
email="you@example.com",
32+
name="Your Name",
33+
company="Optional Co.",
34+
)
35+
print(result["api_key"]) # cvl_… — shown ONCE, store it securely
36+
37+
asyncio.run(main())
38+
```
39+
40+
Export it as `CONVILYN_DEVELOPER_KEY` (see Environment Variables below).
41+
A 503 `REGISTRATION_STORE_UNAVAILABLE` response means the platform side
42+
is temporarily unavailable — retry later; it is not a problem with your
43+
request.
44+
45+
### Step 1 — scaffold a tool server
46+
1947
```bash
2048
pip install convilyn-author
2149
convilyn-author init my-server
2250
cd my-server
2351
```
2452

53+
> **No public server?** If your workflow only orchestrates platform
54+
> built-in tools (OCR, document parsing, analysis), you can skip the
55+
> tool-server track entirely and submit the workflow spec server-less:
56+
> `await client.submit_workflow(spec)` — no `server_ids`, no HMAC
57+
> endpoint. See `submit_workflow` in `client.py`.
58+
2559
Edit `server.py`:
2660

2761
```python

‎src/convilyn_sdk/_version.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
hardcoded constant in two places.
1212
"""
1313

14-
__version__ = "2.1.1b4"
14+
__version__ = "2.2.0b1"

‎src/convilyn_sdk/cli/main.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ async def _deploy() -> dict[str, Any]:
314314
try:
315315
result = asyncio.run(_deploy())
316316
except ConvilynClientError as e:
317-
if e.status_code == 501:
317+
# 503 = HOSTED_NOT_AVAILABLE on current platform builds; 501 kept
318+
# for older builds (TS CLI applies the same pairing).
319+
if e.status_code in (501, 503):
318320
click.echo(
319321
f"Error: hosted runtime not yet available on this platform "
320322
f"({e.detail}). Use `convilyn-author push --endpoint-url ...` "

‎src/convilyn_sdk/client.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from __future__ import annotations
2424

25+
from collections.abc import Sequence
2526
from typing import TYPE_CHECKING, Any
2627

2728
import httpx
@@ -225,21 +226,24 @@ async def deactivate_server(self, server_id: str) -> dict[str, Any]:
225226
async def submit_workflow(
226227
self,
227228
workflow_spec: dict[str, Any],
228-
server_ids: list[str],
229+
server_ids: Sequence[str] = (),
229230
) -> dict[str, Any]:
230231
"""Submit a workflow spec for validation and registration.
231232
232233
Args:
233234
workflow_spec: Compiled workflow spec dict (from WorkflowSpec.compile()).
234-
server_ids: List of server_ids whose tools this workflow uses.
235+
server_ids: server_ids whose tools this workflow uses. Omit (or
236+
pass an empty sequence) for a "server-less" spec that
237+
orchestrates only platform built-in tools — no self-hosted
238+
tool server required.
235239
236240
Returns:
237241
Dict with workflow_id, spec_id, and status.
238242
"""
239243
return await self._request(
240244
"POST",
241245
"/developers/workflows",
242-
json={"workflow_spec": workflow_spec, "server_ids": server_ids},
246+
json={"workflow_spec": workflow_spec, "server_ids": list(server_ids)},
243247
)
244248

245249
async def list_workflows(self) -> list[dict[str, Any]]:
@@ -299,8 +303,10 @@ async def deploy_hosted_runtime(
299303
Raises:
300304
ConvilynClientError: backend failure. Backend code
301305
``HOSTED_NOT_AVAILABLE`` surfaces as a plain
302-
``ConvilynClientError`` with status 501 — callers can
303-
catch it and fall back to ``submit_server`` + BYO
306+
``ConvilynClientError`` with status **503** (older
307+
platform builds answered 501; environments where the
308+
author-runtime router is unmounted answer 404) — callers
309+
can catch it and fall back to ``submit_server`` + BYO
304310
``--endpoint-url`` if their CI needs a hard guarantee.
305311
"""
306312
payload: dict[str, Any] = {

‎src/convilyn_sdk/workflow_types.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class MCPConfigModel(BaseModel):
129129
)
130130
tools: list[str] = Field(
131131
default_factory=list,
132+
max_length=20,
132133
description='Tool references in "server:tool" format',
133134
)
134135

@@ -182,7 +183,7 @@ class WorkflowBlueprint(BaseModel):
182183

183184
spec_id: str
184185
version: str
185-
name: str
186+
name: str = Field(max_length=80)
186187
description: str | None = None
187188
description_i18n: dict[str, str] | None = None
188189

‎tests/unit/test_client.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,16 @@ async def test_submit_workflow(self):
296296
result = await client.submit_workflow({"spec_id": "x"}, ["srv_1"])
297297
assert result["workflow_id"] == "wf_1"
298298

299+
@pytest.mark.asyncio
300+
async def test_submit_workflow_serverless_default(self):
301+
"""C3: server_ids may be omitted — a spec built on platform tools
302+
needs no self-hosted server; the wire body carries an empty list."""
303+
client = ConvilynClient(api_key="cvl_test", base_url="http://test")
304+
client._request = AsyncMock(return_value={"workflow_id": "wf_1"})
305+
await client.submit_workflow({"spec_id": "x"})
306+
body = client._request.call_args.kwargs["json"]
307+
assert body["server_ids"] == []
308+
299309
@pytest.mark.asyncio
300310
async def test_list_workflows(self):
301311
client = ConvilynClient(api_key="cvl_test", base_url="http://test")

‎tests/unit/test_workflow.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import tempfile
55
from pathlib import Path
66

7+
import pydantic
8+
import pytest
9+
710
from convilyn_sdk import ToolServer, WorkflowSpec
811

912

@@ -325,6 +328,23 @@ def test_compile_includes_mcp_when_tools_exist(self):
325328
assert "mcp_config" in compiled
326329
assert compiled["mcp_config"]["tools"] == ["s:t"]
327330

331+
def test_compile_accepts_name_at_max_length(self):
332+
compiled = WorkflowSpec("x", name="x" * 80).compile()
333+
assert len(compiled["name"]) == 80
334+
335+
def test_compile_rejects_name_over_max_length(self):
336+
with pytest.raises(pydantic.ValidationError):
337+
WorkflowSpec("x", name="x" * 81).compile()
338+
339+
def test_use_tools_accepts_max_tool_count(self):
340+
spec = WorkflowSpec("x", name="X").use_tools(*[f"s:t{i}" for i in range(20)])
341+
compiled = spec.compile()
342+
assert len(compiled["mcp_config"]["tools"]) == 20
343+
344+
def test_use_tools_rejects_tool_count_over_max(self):
345+
with pytest.raises(pydantic.ValidationError):
346+
WorkflowSpec("x", name="X").use_tools(*[f"s:t{i}" for i in range(21)])
347+
328348
def test_compile_full_workflow(self):
329349
server = _make_server()
330350
compiled = (

0 commit comments

Comments
 (0)