Framework-agnostic SDK for building Cadence AI agent plugins
Project description
Cadence SDK
Framework-agnostic plugin development kit for the Cadence AI platform.
Write your plugin once — it works on LangGraph, OpenAI Agents SDK, and Google ADK without framework-specific code.
Full documentation: jonaskahn.github.io/cadence
Installation
pip install cadence-sdk
Quick Start
Create my_plugin/plugin.py:
from cadence_sdk import BasePlugin, BaseAgent, PluginMetadata, uvtool, plugin_settings, UvTool
from typing import List
class MyAgent(BaseAgent):
def initialize(self, config: dict) -> None:
self.api_key = config["api_key"]
self._search_tool = self._make_search_tool()
def _make_search_tool(self) -> UvTool:
@uvtool
def search(query: str) -> str:
"""Search for information."""
return call_api(query, self.api_key)
return search
def get_tools(self) -> List[UvTool]:
return [self._search_tool]
def get_system_prompt(self) -> str:
return "You are a helpful assistant."
@plugin_settings([
{"key": "api_key", "type": "str", "description": "API key", "sensitive": True, "required": True},
{"key": "timeout", "type": "int", "description": "Request timeout", "default": 30},
])
class MyPlugin(BasePlugin):
@staticmethod
def get_metadata() -> PluginMetadata:
return PluginMetadata(
pid="com.example.my_plugin",
name="My Plugin",
version="1.0.0",
description="Does something useful",
stateless=True,
)
@staticmethod
def create_agent() -> BaseAgent:
return MyAgent()
Cadence auto-discovers any BasePlugin subclass in a plugin.py file — no manual registration needed.
Package and upload:
zip -r my_plugin.zip my_plugin/ -x "**/__pycache__/*" "**/*.pyc"
curl -X POST http://localhost:8888/api/plugins/system \
-H "Authorization: Bearer <token>" \
-F "file=@my_plugin.zip"
Core Concepts
| Concept | Description |
|---|---|
BasePlugin |
Factory class — declares metadata and creates agent instances |
BaseAgent |
Provides tools and system prompt; receives resolved settings via initialize() |
@uvtool |
Wraps a sync or async function as a framework-agnostic tool |
@plugin_settings |
Declares the settings schema shown in the Cadence UI |
PluginMetadata |
Declares pid, name, version, description, stateless, dependencies |
PluginMetadata fields
| Field | Type | Required | Description |
|---|---|---|---|
pid |
str | Yes | Reverse-domain unique ID, e.g. com.example.my_plugin |
name |
str | Yes | Human-readable display name |
version |
str | Yes | Semantic version string |
description |
str | Yes | Human-readable description |
stateless |
bool | No | True enables instance sharing (default: True) |
agent_type |
str | No | Agent category tag (default: "specialized") |
capabilities |
List[str] | No | Capability tags for filtering |
dependencies |
List[str] | No | Pip requirements, e.g. ["requests>=2.28"] |
sdk_version |
str | No | Compatible SDK range (default: ">=2.0.0,<4.0.0") |
@plugin_settings field types
"str", "int", "float", "bool", "list", "dict"
Each entry: key (required), type (required), description (required), name, default, required, sensitive.
@uvtool options
| Parameter | Description |
|---|---|
name |
Tool name (default: function name) |
description |
Tool description (default: docstring) |
args_schema |
Pydantic model for argument validation |
stream |
If True, stream tool result to client before synthesizer |
stream_filter |
Callable to filter result before streaming (e.g. expose only url, product_id) |
validate |
If True, marks tool for LLM validation |
cache |
True, False, or CacheConfig for semantic caching |
Examples
| Example | Description |
|---|---|
web_search_agent |
Web search via Serper.dev; site/time filters, image search |
recommendation_agent |
Product recommendations via Qdrant hybrid search; dense + sparse embeddings |
Each example includes a full plugin, agent, tools, and README with packaging instructions.
Agent Pattern — Tools as Closures
Tools that need agent state should be created as closures inside factory methods:
class MyAgent(BaseAgent):
def __init__(self):
self.api_key = None
self._search_tool = self._make_search_tool()
def _make_search_tool(self) -> UvTool:
@uvtool
def search(query: str) -> str:
"""Search using agent's API key."""
return call_api(query, self.api_key) # captures self
return search
Configurable System Prompt
To make the system prompt configurable via plugin settings:
-
Add
system_promptto @plugin_settings:{"key": "system_prompt", "name": "System Prompt Override", "type": "str", "required": False, "description": "Optional override for the agent system prompt. Leave empty to use default."}
-
In
initialize(), store the value:self._system_prompt = config.get("system_prompt") -
In
get_system_prompt(), return the override or default:return self._system_prompt or self._default_system_prompt
Async Tools
@uvtool
async def fetch(url: str) -> str:
"""Fetch URL asynchronously."""
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
return await r.text()
result = await fetch.ainvoke(url="https://example.com")
Packaging & Deployment
# Create a deployable zip
zip -r my_plugin.zip path/to/my_plugin/ -x "**/__pycache__/*" "**/*.pyc"
# Upload to Cadence
curl -X POST http://localhost:8888/api/plugins/system \
-H "Authorization: Bearer <token>" \
-F "file=@my_plugin.zip"
Validation & Dependency Utilities
from cadence_sdk import validate_plugin_structure, validate_plugin_structure_shallow, check_dependency_installed, install_dependencies
# Deep validation (checks all required methods and metadata)
is_valid, errors = validate_plugin_structure(MyPlugin)
# Shallow validation (checks metadata fields only)
is_valid, errors = validate_plugin_structure_shallow(MyPlugin)
# Check / install dependencies
if not check_dependency_installed("requests"):
install_dependencies(["requests>=2.28"])
Best Practices
- Set
stateless=Truewhen agents carry no mutable state — enables bundle sharing across orchestrators - Declare
dependenciesinPluginMetadataso the platform can auto-install them - Implement
async cleanup()on agents that hold connections or file handles - Use
validate_dependencies()to surface missing env vars or packages at startup
Development
git clone https://github.com/jonaskahn/cadence.git
cd cadence/sdk
poetry install --with dev
# Run tests
PYTHONPATH=src python -m pytest tests/ -v
# Run example
PYTHONPATH=src python examples/test_sdk.py
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cadence_sdk-2.0.8.tar.gz.
File metadata
- Download URL: cadence_sdk-2.0.8.tar.gz
- Upload date:
- Size: 25.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
950e1a6b6715a2246169f67dc950cf1abdc4261b678ac100172d474266b35985
|
|
| MD5 |
db23d16ce0059cfe3ba9e0f8844cdad5
|
|
| BLAKE2b-256 |
75bade8a48e5dbf8996fcc58cf514e395685163468273eeb2ec6bab0a4fdcaad
|
File details
Details for the file cadence_sdk-2.0.8-py3-none-any.whl.
File metadata
- Download URL: cadence_sdk-2.0.8-py3-none-any.whl
- Upload date:
- Size: 32.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80a1e7bb9ef186ee0f971b91b730194c5595b9e3bb75e9991cc729eccf2d4ed7
|
|
| MD5 |
2bb01de527cf611ddc4c0893f1ce9d7d
|
|
| BLAKE2b-256 |
ca9bc04d0710b601a9c21d5e25de05807e2571bf471c3e59da72ad7f78bca873
|