Observed since 21 Oct, 10:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
AI Execution Layer SDK (contracts + registry + decorators) with curated facades.
aiel-sdk is the local development contract for the AI Execution Layer.
It provides the stable Python interface used to define tools, agents, flows, HTTP handlers, MCP servers, integration calls, and memory operations that can later execute inside the AIEL Execution Plane.
The SDK does not replace the runtime.
The SDK defines what can be executed.
The Execution Plane decides how, where, and under which policies it is executed.
AI applications are easy to prototype locally.
They become difficult to operate when they need to:
The AIEL SDK solves this by giving developers a contract-first programming model.
Developers define executable capabilities locally.
The platform packages, validates, registers, and executes those capabilities remotely through aiel-runtime.
AIEL is composed of multiple layers.
| Component | Responsibility |
|---|---|
aiel-sdk | Defines the local programming contract |
aiel-cli | Packages, validates, versions, and pushes project snapshots |
aiel-runtime | Loads snapshots and executes registered capabilities |
Execution Plane | Provides governed runtime execution |
Control Plane | Manages workspaces, projects, policies, releases, and teams |
Data Plane | Stores snapshots, manifests, traces, memory, and execution state |
The SDK is the developer-facing contract layer.
It is used before runtime execution, but its definitions become the runtime execution surface.
The SDK is designed around explicit exports.
An export is a capability that can be discovered, validated, registered, and executed by the platform.
Supported export types:
| Export Type | Purpose |
|---|---|
| Tool | A discrete callable action |
| Agent | A stateful reasoning or orchestration step |
| Router | A routing decision function |
| Flow | A higher-level workflow entry point |
| HTTP Handler | A runtime-exposed HTTP endpoint |
| MCP Server | A governed tool surface exposed through MCP metadata |
Exports are registered through decorators.
When entry_point.py is imported, the SDK registers those exports into an in-memory registry.
The runtime later uses the same registry contract to discover executable capabilities.
┌──────────────────────┐
│ Developer Code │
│ entry_point.py │
└──────────┬───────────┘
│
│ decorators
▼
┌──────────────────────┐
│ AIEL SDK Registry │
│ tools / agents / │
│ routers / flows / │
│ http / mcp │
└──────────┬───────────┘
│
│ validate_contracts()
▼
┌──────────────────────┐
│ Contract Validation │
└──────────┬───────────┘
│
│ aiel push
▼
┌──────────────────────┐
│ Snapshot Packaging │
└──────────┬───────────┘
│
▼
┌─────────────────────────────┐
│ Execution Plane │
├─────────────────────────────┤
│ Runtime Hydration │
│ Registry Loading │
│ Policy Enforcement │
│ Integration Resolution │
│ Memory Access │
│ Execution Routing │
│ Trace Collection │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ Governed Runtime Execution │
└─────────────────────────────┘
| Local SDK Responsibility | Runtime / Platform Responsibility |
|---|---|
| Define exports | Load registered exports |
| Validate function contracts | Enforce runtime policies |
| Provide typed interfaces | Resolve governed integrations |
| Support local testing | Execute inside managed runtime |
| Provide facade imports | Pin approved runtime dependencies |
| Provide local memory helpers | Persist remote memory |
| Build client requests | Enforce workspace/project scope |
| Register MCP metadata | Expose governed MCP capability surfaces |
The SDK keeps local development aligned with production execution.
Use this for core decorators, registry, contracts, client primitives, and local development.
pip install aiel-sdk
Use this when developing with curated optional integrations.
pip install "aiel-sdk[all]"
The all extra includes curated support for:
From a repository checkout:
pip install -e .
With all optional dependencies:
pip install -e ".[all]"
For zsh, always quote extras:
pip install -e ".[all]"
pip 21+Create an entry_point.py.
The runtime imports this file to discover executable exports.
from aiel_sdk import tool, agent, router, flow, http, mcp_server
from aiel_sdk.context import Context
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
email = str(payload.get("email") or "").strip().lower()
ctx.log("normalize_email", email=email)
return {"email": email}
@agent("collect_personal_data")
def collect_personal_data(ctx: Context, state: dict) -> dict:
out = normalize_email(ctx, {"email": state.get("email")})
return {**dict(state), **out}
@router("next_step")
def next_step(ctx: Context, state: dict) -> str:
return "done" if state.get("email") else "collect_personal_data"
@flow("driver_onboarding")
def driver_onboarding(ctx: Context, input: dict) -> dict:
state = collect_personal_data(ctx, input)
return {"status": "ok", "state": state}
@http.post("/driver/onboard")
def http_driver_onboard(ctx: Context, body: dict) -> dict:
return driver_onboarding(ctx, body)
mcp_server("driver_support", tools=["normalize_email"])
Importing entry_point.py registers all exports.
from aiel_sdk import get_registry, reset_registry, validate_contracts
import entry_point
validate_contracts()
reg = get_registry()
print("tools:", sorted(reg.tools.keys()))
print("agents:", sorted(reg.agents.keys()))
print("routers:", sorted(reg.routers.keys()))
print("flows:", sorted(reg.flows.keys()))
print("http:", [(h.method, h.path) for h in reg.http_handlers])
print("mcp servers:", sorted(reg.mcp_servers.keys()))
reset_registry()
Use reset_registry() between test runs when multiple import cycles are expected.
The registry is the SDK’s local source of truth for exported capabilities.
When decorators are evaluated, the SDK records metadata about each export.
The registry supports:
Supported registry groups:
| Registry Group | Created By |
|---|---|
tools | @tool(...) |
agents | @agent(...) |
routers | @router(...) |
flows | @flow(...) |
http_handlers | @http.get(...), @http.post(...) |
mcp_servers | mcp_server(...) |
A tool is a discrete action.
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
return {"email": str(payload["email"]).strip().lower()}
Recommended signature:
(ctx: Context, payload: dict) -> dict
Use tools for:
An agent is a stateful reasoning or orchestration step.
@agent("collect_personal_data")
def collect_personal_data(ctx: Context, state: dict) -> dict:
out = normalize_email(ctx, {"email": state.get("email")})
return {**state, **out}
Recommended signature:
(ctx: Context, state: dict) -> dict
Use agents for:
A router returns the next execution path.
@router("next_step")
def next_step(ctx: Context, state: dict) -> str:
return "done" if state.get("email") else "collect_personal_data"
Recommended signature:
(ctx: Context, state: dict) -> str
Use routers for:
A flow is a top-level workflow entry point.
@flow("driver_onboarding")
def driver_onboarding(ctx: Context, input: dict) -> dict:
state = collect_personal_data(ctx, input)
return {"status": "ok", "state": state}
Recommended signature:
(ctx: Context, input: dict) -> dict
Use flows for:
HTTP handlers expose flows or tools through runtime-managed HTTP routes.
@http.post("/driver/onboard")
def http_driver_onboard(ctx: Context, body: dict) -> dict:
return driver_onboarding(ctx, body)
Supported pattern:
@http.get("/path")
@http.post("/path")
Use HTTP handlers when:
MCP server metadata declares which tools should be exposed through an MCP-compatible surface.
mcp_server("driver_support", tools=["normalize_email"])
Use MCP metadata when:
Every executable export receives a Context.
The context provides the runtime boundary between user code and platform services.
Typical context responsibilities include:
Example:
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
email = str(payload.get("email") or "").strip().lower()
ctx.log("normalize_email", email=email)
return {"email": email}
AielClient is the SDK client for calling AIEL APIs.
It is used for:
AielClient resolves credentials in this order:
api_key= passed directlyAIEL_TOKEN environment variable("aiel", "default"), when availableExample:
from aiel_sdk import AielClient
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
workspace_id, project_id = client.resolve_scope()
Workspace and project can also be resolved from:
AIEL_WORKSPACE_ID
AIEL_PROJECT_ID
Integrations are accessed through governed platform connections.
Application code should not embed raw third-party credentials.
Instead, it references platform-managed connection IDs.
This allows the platform to enforce:
from aiel_sdk import AielClient
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
connections = client.integrations.list("ws_123", "proj_456")
connection = connections[0]
detail = client.integrations.get(
connection.workspace_id,
connection.project_id,
connection.connection_id,
)
result = client.integrations.invoke_action(
connection.workspace_id,
connection.project_id,
connection.connection_id,
action="slack.post_message",
payload={
"params": {
"channel": "#general",
"text": "Hello from AIEL SDK",
}
},
)
Typed wrappers provide provider-specific request and response models on top of governed platform connections.
from aiel_sdk import AielClient
from aiel_sdk.integrations.slack import RemoteSlack, SlackPostMessageRequest
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
slack = RemoteSlack(client, connection_id="conn_slack_123")
slack.post_message(
SlackPostMessageRequest(
channel="#general",
text="Hello from AIEL SDK",
)
)
from aiel_sdk import AielClient
from aiel_sdk.integrations.openai import RemoteOpenAI, OpenAIResponsesCreateRequest
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
oai = RemoteOpenAI(client, connection_id="conn_openai_123")
result = oai.responses_create(
OpenAIResponsesCreateRequest(
input="Say hello",
model="gpt-4o-mini",
)
)
print(result.raw)
The SDK supports both local and remote memory implementations.
Use local memory for development and tests.
Use remote memory for production workflows running through AIEL-managed workspace/project scope.
from aiel_sdk.memory import InMemorySaver, ThreadMessage
saver = InMemorySaver()
saver.thread_write(
thread_key="t1",
messages=[
ThreadMessage(role="human", content="hi"),
],
)
thread = saver.thread_read(thread_key="t1")
print(thread.messages[-1].content)
Local memory:
from aiel_sdk import AielClient
from aiel_sdk.memory import RemoteSaver, ThreadMessage, RecallQuery
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
remote = RemoteSaver(
client,
workspace_id="ws_123",
project_id="proj_456",
)
remote.thread_write(
thread_key="t1",
messages=[
ThreadMessage(role="human", content="hi"),
],
ttl_seconds=3600,
)
state = remote.state_patch(
thread_key="t1",
patch={"step": "triage"},
ttl_seconds=3600,
)
checkpoint = remote.checkpoint_write(
thread_key="t1",
state=state.state,
metadata={"node": "triage_agent"},
ttl_seconds=86400,
)
items = remote.recall_search(
query=RecallQuery(
namespace=["users", "u1"],
limit=10,
)
)
Remote memory supports:
The SDK provides curated facade modules for approved runtime dependencies.
Facade imports are used to keep local development aligned with managed runtime environments.
| Capability | Import Path | Purpose |
|---|---|---|
| Pydantic | aiel_core.pydantic | Data validation and serialization |
| LangGraph | aiel_core.langgraph.graph | State graph orchestration |
| LangChain Core | aiel_core.langchain.core | Prompt templates and runnables |
| LangChain Messages | aiel_core.langchain.messages | AI/Human message types |
| LangSmith | aiel_core.langsmith.client | Observability and tracing |
| AIEL CLI Context | aiel_core.aiel_cli.context.user_context | CLI context helpers |
Example:
from aiel_core.pydantic import BaseModel, Field
class NormalizeEmailPayload(BaseModel):
email: str = Field(..., description="Raw email address")
LangGraph example:
from aiel_core.langgraph.graph import StateGraph, START, END, MessagesState
graph = StateGraph(MessagesState)
graph.add_node("process", lambda state: state)
graph.add_edge(START, "process")
graph.add_edge("process", END)
app = graph.compile()
result = app.invoke({"messages": []})
Missing optional dependencies fail with actionable errors that identify the required installation extra.
In production, application code is executed by aiel-runtime inside the Execution Plane.
The runtime performs the following sequence:
entry_point.pyRuntime invocation is handled by the platform.
Local code should be written against the SDK contract, not against runtime internals.
Developer
│
│ writes entry_point.py
▼
AIEL SDK
│
│ decorators register exports
▼
Local Registry
│
│ validate_contracts()
▼
AIEL CLI
│
│ aiel push
▼
Snapshot
│
│ versioned + immutable
▼
Execution Plane
│
│ aiel-runtime loads registry
▼
Governed Execution
│
├── policies
├── integrations
├── memory
├── traces
└── audit logs
The Execution Plane may provide curated runtime bundles with approved dependency sets.
aiel-runtime==X.Y.Z
aiel-sdk==X.Y.Z
Used for:
aiel-runtime[ai]==X.Y.Z
May include curated AI orchestration dependencies such as:
Runtime identifiers should be allowlisted by the platform.
Package versions should be pinned by runtime bundle.
Project snapshots should include manifest metadata such as:
| Field | Purpose |
|---|---|
runtime | Runtime bundle or runtime image identifier |
sdk_version | SDK contract version |
entry_point | Python module loaded by runtime |
exports | Registered tools, agents, flows, HTTP handlers, MCP metadata |
requirements | Dependency metadata |
snapshot_id | Immutable deployment identifier |
created_at | Snapshot creation timestamp |
The manifest is used by the Execution Plane to determine how the snapshot should be loaded and executed.
my-aiel-project/
├── entry_point.py
├── tools/
│ ├── __init__.py
│ └── validation.py
├── agents/
│ ├── __init__.py
│ └── onboarding.py
├── flows/
│ ├── __init__.py
│ └── driver_onboarding.py
├── models/
│ ├── __init__.py
│ └── schemas.py
├── tests/
│ └── test_contracts.py
├── requirements.txt
└── README.md
Use Pydantic models for validation at the boundary of tools, agents, and flows.
from aiel_core.pydantic import BaseModel, Field
from aiel_sdk import tool
from aiel_sdk.context import Context
class NormalizeEmailPayload(BaseModel):
email: str = Field(..., description="Raw email address")
class NormalizeEmailResult(BaseModel):
email: str
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
data = NormalizeEmailPayload.model_validate(payload)
email = data.email.strip().lower()
return NormalizeEmailResult(email=email).model_dump()
Recommended practice:
Use ctx.log(...) for structured runtime logs.
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
email = str(payload.get("email") or "").strip().lower()
ctx.log("normalize_email.completed", email=email)
return {"email": email}
Logs should be:
Prefer explicit errors and structured return values.
@tool("safe_normalize_email")
def safe_normalize_email(ctx: Context, payload: dict) -> dict:
try:
email = str(payload.get("email") or "").strip().lower()
if not email:
return {
"status": "invalid",
"reason": "email_required",
}
return {
"status": "ok",
"email": email,
}
except Exception as exc:
ctx.log("safe_normalize_email.failed", error=str(exc))
return {
"status": "failed",
"reason": "unexpected_error",
}
Avoid leaking:
python -m unittest discover -s tests -v
From a repository checkout:
PYTHONPATH=src python -m aiel_sdk.examples.smoke
def test_contracts_are_valid():
import entry_point
from aiel_sdk import get_registry, validate_contracts
validate_contracts()
reg = get_registry()
assert "normalize_email" in reg.tools
assert "driver_onboarding" in reg.flows
entry_point.py SmallUse entry_point.py as the registration surface.
Move business logic into modules.
Recommended:
# entry_point.py
from aiel_sdk import tool
from tools.email import normalize_email_impl
@tool("normalize_email")
def normalize_email(ctx, payload):
return normalize_email_impl(ctx, payload)
Do not store provider credentials in application code.
Use AIEL connection IDs and governed integration wrappers.
Recommended:
slack = RemoteSlack(client, connection_id="conn_slack_123")
Avoid:
# Do not do this
slack_token = "xoxb-..."
Use InMemorySaver for tests.
Use RemoteSaver for production.
Do not assume local memory behavior matches durable remote state semantics.
Export names become part of the execution contract.
Changing names can affect:
Recommended:
@flow("driver_onboarding")
Avoid frequently renaming exports after they are used by production callers.
Return structured dictionaries with clear status fields.
Recommended:
return {
"status": "ok",
"state": state,
}
Avoid ambiguous responses:
return state
The SDK supports the platform security model by avoiding direct credential ownership in application code.
Security boundaries are enforced by the platform, including:
The SDK should be treated as the local contract layer, not the policy enforcement engine.
Policy enforcement occurs in the managed platform and runtime environment.
When used with AIEL CLI and AIEL Runtime, the SDK enables:
Example error:
ImportError: aiel_core.langgraph is not available
Install the required extra:
pip install "aiel-sdk[langgraph]"
Or install all curated integrations:
pip install "aiel-sdk[all]"
Typical cause:
Check signatures:
| Export Type | Expected Pattern |
|---|---|
| Tool | (ctx, payload: dict) -> dict |
| Agent | (ctx, state: dict) -> dict |
| Router | (ctx, state: dict) -> str |
| Flow | (ctx, input: dict) -> dict |
| HTTP POST | (ctx, body: dict) -> dict |
| HTTP GET | (ctx, query: dict) -> dict |
Possible causes:
entry_point.py was not importedFix:
import entry_point
from aiel_sdk import get_registry
reg = get_registry()
print(reg.tools.keys())
AielClient could not resolve credentials.
Check one of the following:
api_key=...AIEL_TOKENSet scope explicitly:
client = AielClient(
base_url="https://api.example.com",
api_key="REPLACE_ME",
workspace_id="ws_123",
project_id="proj_456",
)
Or use environment variables:
export AIEL_WORKSPACE_ID=ws_123
export AIEL_PROJECT_ID=proj_456
When moving existing AI application code to AIEL:
@http.aiel-cli.The SDK defines the contract.
The CLI deploys the contract.
Typical workflow:
pip install aiel-sdk
pip install aiel-cli
aiel auth login
aiel config set workspace <workspace-slug>
aiel config set project <project-slug>
aiel init
aiel add .
aiel commit -m "Add driver onboarding flow"
aiel push
The runtime executes the contract.
At runtime:
entry_point.py is importedDevelopers should not depend on runtime internals directly.
Use SDK interfaces for local development and runtime compatibility.
Version history is managed through Git tags and PyPI releases.
See LICENSE.
aiel-sdk is the contract layer for governed AI execution.
It lets teams define tools, agents, flows, HTTP handlers, MCP surfaces, integration calls, and memory operations locally while keeping production execution under the control of the AIEL Execution Plane.
The SDK gives developers a stable programming model.
The platform provides the runtime, governance, isolation, and operational controls required for production AI systems.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.8.0 | 14 May 2026 | 8 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 10:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:5c596f0ee939e8d12e670b2f9c85a29319b50b70bc9dca2f7430bd445c40d8db