Types
Pydantic models you read or return from tool functions, plus the compliance-report types the testing helpers emit.
from convilyn_sdk import (
ToolSpec,
ToolResult,
ToolError,
ToolDataRef,
ComplianceReport,
ComplianceResult,
)
ToolSpec
Manifest-level description of one tool. Built automatically by @server.tool from the function signature — you rarely construct it by hand, but you may want to read it.
class ToolSpec(BaseModel):
name: str
description: str
input_schema: dict[str, Any]
idempotent: bool = False
output_schema: dict[str, Any] | None = None
| Field | Type | Notes |
|---|---|---|
name | str | Tool ID — e.g. analyze_text; the platform exposes it as <server>__<name> |
description | str | Human-facing summary; surfaced in the catalogue UI |
input_schema | dict[str, Any] | JSON Schema derived from the function's type hints |
idempotent | bool | True ⇒ platform may safely retry the tool without side effects |
output_schema | dict[str, Any] | None | Optional explicit output schema; None accepts any well-formed result |
ToolResult
What your tool function returns. Tools that need to stash large payloads return ToolDataRef shapes; tools that return small data can use ToolResult.ok(data) and ToolResult.fail(code, message).
class ToolResult(BaseModel):
success: bool = True
data: dict[str, Any] | None = None
error: ToolError | None = None
execution_time_ms: float | None = None
@classmethod
def ok(cls, data: dict[str, Any]) -> ToolResult: ...
@classmethod
def fail(cls, code: str, message: str, details: dict[str, Any] | None = None) -> ToolResult: ...
The two class-method constructors are the idiomatic shape:
from convilyn_sdk import ToolResult
@server.tool(description="Lookup a record by ID")
async def lookup(record_id: str) -> ToolResult:
record = await db.get(record_id)
if record is None:
return ToolResult.fail("NOT_FOUND", f"No record for {record_id!r}")
return ToolResult.ok({"record": record})
Returning a plain dict[str, Any] is also supported — the SDK lifts it into ToolResult(success=True, data=...) automatically. Return ToolResult only when you need explicit failure handling or you want to set execution_time_ms.
ToolError
class ToolError(BaseModel):
code: str
message: str
details: dict[str, Any] | None = None
code is the only structural field; message is the human-readable summary; details is a free-form bag the platform forwards to retry logic. The error code is matched against RetryPolicy.classify_failures to determine retry behaviour.
ToolDataRef
class ToolDataRef(BaseModel):
ref_id: str
summary: str
Returned by tools that stash large payloads in the data store. The LLM sees only the summary; the platform retrieves the full payload later via ctx.data_store.get(ref_id) when downstream tools or the final assembly step need it.
from convilyn_sdk import ToolContext
@server.tool(description="Crawl + summarise")
async def crawl(ctx: ToolContext, url: str) -> dict:
full = await my_crawler.fetch(url)
ref_id = await ctx.data_store.store(full)
return {"ref_id": ref_id, "summary": f"Fetched {len(full['pages'])} pages from {url}"}
ComplianceReport + ComplianceResult
The compliance-test harness in convilyn_sdk.testing emits these. Use them in CI to assert that a server / workflow combination passes every platform-side compliance check before deploy.
class ComplianceResult(BaseModel):
passed: bool
check_name: str
message: str
details: dict[str, Any] = Field(default_factory=dict)
class ComplianceReport(BaseModel):
results: list[ComplianceResult] = Field(default_factory=list)
@property
def all_passed(self) -> bool: ...
@property
def failed(self) -> list[ComplianceResult]: ...
from convilyn_sdk.testing import run_compliance
report = await run_compliance(server, workflow)
assert report.all_passed, [r.message for r in report.failed]
Versioning
Every type above is part of the v1.x public surface. Additive changes (new optional fields) are non-breaking; removals or required-field additions ship under a major bump.