Cadence SDK - To building custom AI agent plugins for Cadence AI Framework
Project description
Cadence SDK ๐ค
Plugin Development Framework for Cadence AI Multi-Agent System
The Cadence SDK provides a comprehensive framework for building custom AI agent plugins that integrate seamlessly with the Cadence multi-agent conversational AI system. Built on LangChain and LangGraph, it offers powerful abstractions for creating intelligent, tool-enabled agents with automatic routing and orchestration.
๐ Features
- ๐ Plugin Architecture: Simple, extensible plugin system with automatic discovery
- ๐ค Agent Framework: Complete agent lifecycle management with LangGraph integration
- ๐ ๏ธ Tool System: Easy tool creation with the
@tooldecorator - ๐ State Management: Comprehensive state handling with type safety
- ๐ Automatic Routing: Intelligent agent-to-agent communication and routing
- โก Parallel Execution: Support for parallel tool calls for improved performance
- ๐ Validation: Built-in validation for plugins, agents, and tools
- ๐ Type Safety: Full type hints and TypedDict support for robust development
- ๐ฅ Health Checks: Built-in health monitoring and dependency validation
- ๐ฆ Registry System: Centralized plugin discovery and management
๐ฆ Installation
From PyPI (Recommended)
pip install cadence-sdk
From Source
git clone https://github.com/jonaskahn/cadence-sdk.git
pip install -e .
Development Installation
git clone https://github.com/jonaskahn/cadence-sdk.git
pip install -e ".[dev]"
๐ฏ Quick Start
Basic Plugin Structure
from cadence_sdk import BaseAgent, BasePlugin, PluginMetadata, tool
from typing_extensions import Annotated, TypedDict
class SearchResponseSchema(TypedDict):
"""Schema for structured search responses"""
title: Annotated[str, "Primary identifier or headline"]
description: Annotated[str, "Contextual summary or metadata"]
link: Annotated[str, "Canonical URI to the source"]
thumbnail_link: Annotated[str, "Optional visual representation URI"]
class SearchPlugin(BasePlugin):
@staticmethod
def get_metadata() -> PluginMetadata:
return PluginMetadata(
name="browse_internet",
version="1.3.1",
description="Internet Browser Search agent, using DuckDuckGo API",
agent_type="specialized",
response_schema=SearchResponseSchema,
response_suggestion="When presenting search results, always include source citations with clickable links",
capabilities=["web_search", "news_search", "image_search"],
llm_requirements={
"provider": "openai",
"model": "gpt-4.1",
"temperature": 0.2,
"max_tokens": 1024,
},
dependencies=["cadence-sdk>=1.3.1,<2.0.0", "ddgs>=9.5.4,<10.0.0"],
)
@staticmethod
def create_agent() -> BaseAgent:
return SearchAgent(SearchPlugin.get_metadata())
@staticmethod
def health_check() -> dict:
return {
"healthy": True,
"details": "Search plugin is operational",
"checks": {"search_engine": "OK", "dependencies": "OK"},
}
class SearchAgent(BaseAgent):
def __init__(self, metadata: PluginMetadata):
super().__init__(metadata)
def get_tools(self):
from .tools import search_tools
return search_tools
def get_system_prompt(self) -> str:
return """You are the Search Agent, specialized in web search and information retrieval.
Your responsibilities:
- Understand user search intent and information needs
- Choose the most appropriate search tool for each query
- Execute targeted searches with relevant keywords
- Analyze and summarize search results clearly
- Provide comprehensive, well-organized responses
- Cite sources when presenting information
- Always use the provided search decorators to find current information
"""
@tool
def web_search(query: str) -> str:
"""Search the web for information using DuckDuckGo.
Args:
query: The search query
Returns:
Search results from the web
"""
try:
from ddgs import DDGS
results = DDGS().text(query)
return results[:500]
except Exception as e:
return f"Web search error: {str(e)}"
Plugin Discovery
Plugins are automatically discovered by the Cadence framework through the SDK's plugin discovery system. No manual registration is required - the framework scans for plugins that inherit from BasePlugin.
# Plugins are automatically discovered when imported
from .plugin import SearchPlugin
# Or discover all plugins programmatically
from cadence_sdk import discover_plugins
plugins = discover_plugins()
๐ Core Components
BasePlugin
The foundation of all Cadence plugins. Every plugin must implement:
get_metadata(): Returns plugin metadata and configurationcreate_agent(): Creates and returns the plugin's agent instancevalidate_dependencies(): Optional dependency validation (returns list of error messages)health_check(): Optional health monitoring (returns health status dict)
class MyPlugin(BasePlugin):
@staticmethod
def get_metadata() -> PluginMetadata:
return PluginMetadata(
name="my_plugin",
version="1.0.0",
description="Plugin description",
capabilities=["capability1", "capability2"],
agent_type="specialized", # or "general", "utility"
llm_requirements={
"provider": "openai",
"model": "gpt-4o",
"temperature": 0.1,
"max_tokens": 1024,
},
dependencies=["cadence-sdk>=1.3.1,<2.0.0"],
)
@staticmethod
def create_agent() -> BaseAgent:
return MyAgent(MyPlugin.get_metadata())
@staticmethod
def validate_dependencies() -> List[str]:
"""Validate plugin dependencies."""
errors = []
# Add your validation logic
return errors
@staticmethod
def health_check() -> dict:
"""Perform health check."""
return {"healthy": True, "details": "Plugin is operational"}
BaseAgent
The agent implementation that handles LLM interactions and tool execution:
class MyAgent(BaseAgent):
def __init__(self, metadata: PluginMetadata, parallel_tool_calls: bool = True):
super().__init__(metadata, parallel_tool_calls)
def get_tools(self) -> List[AgentTool]:
"""Return the tools this agent exposes."""
return [tool1, tool2, tool3]
def get_system_prompt(self) -> str:
"""Return the system prompt for this agent."""
return "You are a helpful AI assistant."
def initialize(self) -> None:
"""Initialize agent resources."""
super().initialize()
# Add custom initialization
def cleanup(self) -> None:
"""Cleanup agent resources."""
# Add custom cleanup
PluginMetadata
Comprehensive metadata for plugin configuration:
@dataclass
class PluginMetadata:
name: str
version: str
description: str
capabilities: List[str] = field(default_factory=list)
llm_requirements: Dict[str, Any] = field(default_factory=dict)
dependencies: List[str] = field(default_factory=list)
response_schema: Optional[Type[TypedDict]] = None
response_suggestion: Optional[str] = None
agent_type: str = "specialized" # "specialized", "general", "utility"
sdk_version: str = ">=1.0.1,<2.0.0"
def get_model_config(self) -> ModelConfig:
"""Convert LLM requirements to ModelConfig."""
# Implementation details...
Tool System
Create tools using the @tool decorator:
from cadence_sdk import tool
from ddgs import DDGS
@tool
def web_search(query: str) -> str:
"""Search the web for information using DuckDuckGo.
Args:
query: The search query
Returns:
Search results from the web
"""
try:
results = DDGS().text(query)
return results[:500]
except Exception as e:
return f"Web search error: {str(e)}"
@tool
def search_news(query: str) -> str:
"""Search for recent news articles.
Args:
query: The news search query
Returns:
News search results
"""
try:
results = DDGS().news(query)
return results[:500]
except Exception as e:
return f"News search error: {str(e)}"
@tool
def search_images(query: str) -> str:
"""Search for images and visual content related to the query.
Args:
query: The image search query
Returns:
Description of image search results
"""
try:
results = DDGS().images(query)
return results[:500]
except Exception as e:
return f"Image search error: {str(e)}"
# Export decorators as a list for easy import
search_tools = [web_search, search_news, search_images]
๐ State Management
The SDK provides comprehensive state management with type safety:
AgentState
class AgentState(TypedDict, total=False):
messages: Annotated[Sequence[BaseMessage], add_messages]
thread_id: Optional[str]
current_agent: Optional[str]
agent_hops: Optional[int]
plugin_context: Dict[str, Any]
metadata: Optional[Dict[str, Any]]
multi_agent: Optional[bool]
PluginContext
class PluginContext(TypedDict, total=False):
routing_history: List[str]
consecutive_agent_repeats: int
last_routed_agent: Optional[str]
synthesizer_output: Optional[Dict[str, Any]]
tools_used: List[str]
agent_outputs: Dict[str, Any]
State Helpers
from cadence_sdk.types.state import StateHelpers, RoutingHelpers, PluginContext
# Safe state operations
messages = StateHelpers.safe_get_messages(state)
agent_hops = StateHelpers.safe_get_agent_hops(state)
plugin_context = StateHelpers.get_plugin_context(state)
# Update plugin context
updated_state = StateHelpers.update_plugin_context(
state,
routing_history=["agent1", "agent2"],
tools_used=["tool1", "tool2"]
)
# Routing helpers
context = RoutingHelpers.add_to_routing_history(context, "my_agent")
context = RoutingHelpers.add_tool_used(context, "my_tool")
context = RoutingHelpers.update_consecutive_routes(context, "my_agent")
๐ Validation
Built-in validation for plugins, agents, and tools:
from cadence_sdk.utils.validation import (
validate_plugin_structure,
validate_plugin_structure_shallow,
validate_metadata,
validate_tools,
validate_agent
)
# Validate plugin structure
errors = validate_plugin_structure(MyPlugin)
if errors:
print(f"Validation errors: {errors}")
# Validate metadata
metadata = MyPlugin.get_metadata()
errors = validate_metadata(metadata)
# Validate tools
tools = my_agent.get_tools()
errors = validate_tools(tools)
๐ฅ Health Monitoring
Implement health checks for your plugins:
class MyPlugin(BasePlugin):
@staticmethod
def health_check() -> dict:
"""Perform comprehensive health check."""
try:
# Check dependencies
import requests
import numpy
# Check external services
response = requests.get("https://api.example.com/health", timeout=5)
api_healthy = response.status_code == 200
# Check configuration
config_valid = os.getenv("MY_API_KEY") is not None
return {
"healthy": api_healthy and config_valid,
"details": "All systems operational",
"checks": {
"dependencies": "OK",
"api_connectivity": "OK" if api_healthy else "FAILED",
"configuration": "OK" if config_valid else "MISSING_API_KEY",
},
}
except Exception as e:
return {
"healthy": False,
"details": f"Health check failed: {e}",
"error": str(e),
}
๐ง Advanced Features
Parallel Tool Execution
Enable parallel tool calls for improved performance:
class MyAgent(BaseAgent):
def __init__(self, metadata: PluginMetadata):
# Enable parallel tool calls (default: True)
super().__init__(metadata, parallel_tool_calls=True)
Custom Model Configuration
Configure LLM requirements in plugin metadata:
def get_metadata() -> PluginMetadata:
return PluginMetadata(
name="my_plugin",
version="1.0.0",
description="Plugin with custom LLM config",
llm_requirements={
"provider": "anthropic",
"model": "claude-3-sonnet-20240229",
"temperature": 0.7,
"max_tokens": 2048,
"additional_params": {
"top_p": 0.9,
"frequency_penalty": 0.1,
}
},
)
Response Schemas
Define structured response schemas:
from typing_extensions import Annotated, TypedDict
class SearchResponseSchema(TypedDict):
"""Unified schema for representing heterogeneous search response entities"""
title: Annotated[
str,
"Primary identifier or headline of the search result. For news articles: the article headline; for images: descriptive caption or filename; for general search: the page title or primary heading that encapsulates the content's essence",
]
description: Annotated[
str,
"Contextual summary or metadata providing substantive insight into the search result. For news: article excerpt or lead paragraph; for images: alt text, caption, or contextual description; for general search: meta description or extracted content snippet that offers semantic understanding of the result's relevance",
]
link: Annotated[
str,
"Canonical URI representing the authoritative source location. This serves as the primary navigation endpoint, ensuring consistent access to the original resource regardless of search result type or content modality",
]
thumbnail_link: Annotated[
str,
"Optional visual representation URI for enhanced user experience. For images: compressed preview version; for news articles: featured image or publication logo; for general search: favicon, screenshot, or representative visual element that aids in result recognition and selection",
]
class SearchPlugin(BasePlugin):
@staticmethod
def get_metadata() -> PluginMetadata:
return PluginMetadata(
name="browse_internet",
version="1.3.1",
description="Internet Browser Search agent, using DuckDuckGo API",
response_schema=SearchResponseSchema,
response_suggestion="When presenting search results, always include source citations with clickable links, organize information by relevance and recency, provide context about the credibility of sources, and offer follow-up search suggestions when appropriate.",
)
๐ Project Structure
SDK Structure
cadence_sdk/
โโโ base/ # Core base classes
โ โโโ agent.py # BaseAgent implementation
โ โโโ plugin.py # BasePlugin interface
โ โโโ metadata.py # PluginMetadata and ModelConfig
โ โโโ loggable.py # Logging utilities
โโโ registry/ # Plugin discovery and management
โ โโโ plugin_registry.py # PluginRegistry implementation
โ โโโ contracts.py # Plugin contracts
โโโ decorators/ # Decorator system
โ โโโ tool.py # @tool decorator
โ โโโ schema.py # Schema decorators
โโโ types/ # Type definitions
โ โโโ state.py # AgentState and helpers
โ โโโ messages.py # Message types
โโโ utils/ # Utility functions
โ โโโ validation.py # Validation utilities
โ โโโ helpers.py # General helpers
โ โโโ installers.py # Installation utilities
โ โโโ directory_discovery.py # Directory-based discovery
โ โโโ environment_discovery.py # Environment detection
โโโ examples/ # Example implementations
โโโ template_plugin/ # Template plugin example
Plugin Structure (Real Example)
Based on the search_agent implementation:
search_agent/
โโโ __init__.py # Auto-registration
โโโ plugin.py # Plugin class and metadata
โโโ agent.py # Agent implementation
โโโ tools.py # Tool definitions
Key Files:
__init__.py: Plugin package initialization (usually empty)plugin.py: Contains theBasePluginimplementation with metadataagent.py: Contains theBaseAgentimplementation with system prompttools.py: Contains tool definitions using@tooldecorator
๐งช Testing
The SDK includes comprehensive testing utilities:
# Test plugin structure
def test_plugin_structure():
errors = validate_plugin_structure(MyPlugin)
assert not errors, f"Plugin validation failed: {errors}"
# Test agent creation
def test_agent_creation():
agent = MyPlugin.create_agent()
assert isinstance(agent, BaseAgent)
tools = agent.get_tools()
assert len(tools) > 0
system_prompt = agent.get_system_prompt()
assert isinstance(system_prompt, str)
# Test tool validation
def test_tools():
agent = MyPlugin.create_agent()
tools = agent.get_tools()
errors = validate_tools(tools)
assert not errors, f"Tool validation failed: {errors}"
๐ Integration with Cadence Core
The SDK is designed to integrate seamlessly with the Cadence core system:
- Automatic Discovery: Plugins are automatically discovered and registered
- LangGraph Integration: Agents work as LangGraph nodes out of the box
- State Synchronization: State is automatically synchronized between agents
- Tool Binding: Tools are automatically bound to LLM models
- Routing Logic: Built-in routing decisions for agent-to-agent communication
๐ Examples
Complete Plugin Example
See the examples/template_plugin/ directory for a complete working example that demonstrates:
- Plugin metadata configuration
- Agent implementation with multiple tools
- Tool creation and validation
- Health checks and dependency validation
- Best practices for plugin development
Real-World Plugin Ideas
- Web Search Agent: Search the web and return structured results
- Code Analysis Agent: Analyze code and provide suggestions
- Data Processing Agent: Process and transform data
- API Integration Agent: Integrate with external APIs
- File Management Agent: Handle file operations
- Database Agent: Query and manipulate databases
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
- Fork the repository
- Create a feature branch
- Install development dependencies:
pip install -e ".[dev]" - Run tests:
pytest - Submit a pull request
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Built on LangChain for LLM integration
- Powered by LangGraph for agent orchestration
- Type safety with Pydantic and Python typing
- Plugin architecture inspired by modern plugin systems
๐ Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Read the Docs
Made with โค๏ธ by the Cadence AI Team
Build intelligent agents that work together seamlessly with the Cadence SDK.
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-1.3.1.tar.gz.
File metadata
- Download URL: cadence_sdk-1.3.1.tar.gz
- Upload date:
- Size: 31.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.0 CPython/3.13.7 Darwin/24.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1410eab672710084edc5da28478ec130749cbda2ea25401002695177aceaaaa3
|
|
| MD5 |
a23d89bc82a7c6fb20575f6461a8d75e
|
|
| BLAKE2b-256 |
42955b0c7b1a203e4947c8ea9e825d47583eee880e89fb4137d68a9a4c6c45dc
|
File details
Details for the file cadence_sdk-1.3.1-py3-none-any.whl.
File metadata
- Download URL: cadence_sdk-1.3.1-py3-none-any.whl
- Upload date:
- Size: 34.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.0 CPython/3.13.7 Darwin/24.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ede280a43c019f70550bcebd8d77086c7265651f6e8b4ad98668bcd2c5534cc
|
|
| MD5 |
b0c3fb093a0fd2f396249e70aa84cc48
|
|
| BLAKE2b-256 |
0dfff7745927e48eddbd5f651c0d6ccd2a5a252d08cc5e895c35ccdfc71ae815
|