cadence-sdk


Namecadence-sdk JSON
Version 1.0.7 PyPI version JSON
download
home_pagehttps://github.com/jonaskahn/cadence-sdk
SummaryCadence SDK - To building custom AI agent plugins for Cadence AI Framework
upload_time2025-09-03 09:24:34
maintainerNone
docs_urlNone
authorJonas Kahn
requires_python<3.14,>=3.13
licenseMIT
keywords ai agents cadence-ai cadence_sdk langchain langgraph plugins
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Cadence SDK

A comprehensive SDK for building custom AI agent plugins for the Cadence Framework.

## Overview

The Cadence SDK provides the tools and interfaces needed to create powerful, extensible AI agents that integrate
seamlessly with the Cadence multi-agent framework. Build agents with custom tools, sophisticated reasoning capabilities,
and domain-specific knowledge.

## Features

- **Agent Framework**: Create intelligent agents with custom behavior and system prompts
- **Tool System**: Build and integrate custom tools using the `@tool` decorator
- **Plugin Management**: Easy plugin discovery and registration with automatic loading
- **Type Safety**: Full Python type support with proper annotations
- **Extensible**: Plugin-based architecture for easy extension and customization
- **LangGraph Integration**: Seamless integration with LangGraph workflows
- **LLM Binding**: Automatic tool binding to language models

## Installation

```bash
pip install cadence-sdk
```

## Quick Start

### Key Imports

```python
# Core classes - import from main SDK module (recommended)
from cadence_sdk import BaseAgent, BasePlugin, PluginMetadata, tool, register_plugin

# Alternative: import specific components if needed
from cadence_sdk.base.agent import BaseAgent
from cadence_sdk.base.plugin import BasePlugin
from cadence_sdk.base.metadata import PluginMetadata
from cadence_sdk.tools.decorators import tool
from cadence_sdk import register_plugin, discover_plugins
```

**Note**: The main import approach is recommended for most use cases as it provides all necessary components in one
import statement.

### Creating a Simple Agent

```python
from cadence_sdk import BaseAgent, PluginMetadata, tool


class CalculatorAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        super().__init__(metadata)

    def get_tools(self):
        from .tools import math_tools
        return math_tools

    def get_system_prompt(self) -> str:
        return "You are a calculator agent that helps with mathematical calculations."


@tool
def calculate(expression: str) -> str:
    """Perform mathematical calculations"""
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"Error: {str(e)}"
```

### Plugin Structure

```
my_plugin/
├── __init__.py          # Plugin registration with register_plugin()
├── plugin.py            # Main plugin class (BasePlugin)
├── agent.py             # Agent implementation (BaseAgent)
├── tools.py             # Tool functions with @tool decorator
├── pyproject.toml       # Package configuration
└── README.md            # Documentation
```

**Required Files:**

- `__init__.py`: Must call `register_plugin(YourPlugin)` to auto-register the plugin
- `plugin.py`: Must implement `BasePlugin` with `get_metadata()` and `create_agent()` methods
- `agent.py`: Must implement `BaseAgent` with `get_tools()` and `get_system_prompt()` methods
- `tools.py`: Contains tool functions decorated with `@tool` decorator
- `pyproject.toml`: Package metadata and dependencies

### Plugin Registration

```python
from cadence_sdk import BasePlugin, PluginMetadata


class CalculatorPlugin(BasePlugin):
    @staticmethod
    def get_metadata() -> PluginMetadata:
        return PluginMetadata(
            name="calculator",
            version="1.0.7",
            description="Mathematical calculation plugin",
            capabilities=["mathematics", "calculations"],
            llm_requirements={
                "provider": "openai",
                "model": "gpt-4",
                "temperature": 0.1
            },
            agent_type="specialized",
            dependencies=["cadence_sdk>=1.0.2,<2.0.0"]
        )

    @staticmethod
    def create_agent():
        from .agent import CalculatorAgent
        return CalculatorAgent(CalculatorPlugin.get_metadata())
```

## Configuration

### Plugin Registration

To make your plugin discoverable by the Cadence framework, you need to register it in your plugin's `__init__.py`:

```python
# plugins/src/cadence_example_plugins/my_plugin/__init__.py
from cadence_sdk import register_plugin
from .plugin import MyPlugin

# Register on import
register_plugin(MyPlugin)
```

### Environment Variables

```bash
# Set plugin directories (single path)
export CADENCE_PLUGINS_DIR="./plugins/src/cadence_plugins"

# Or multiple directories as JSON array
export CADENCE_PLUGINS_DIR='["/path/to/plugins", "/another/path"]'

# Plugin limits (configured in main application)
export CADENCE_MAX_AGENT_HOPS=25

export CADENCE_GRAPH_RECURSION_LIMIT=50

# LLM Provider Configuration
export CADENCE_DEFAULT_LLM_PROVIDER=openai
export CADENCE_OPENAI_API_KEY=your-api-key
```

### Plugin Discovery

The SDK automatically discovers plugins from:

- **Environment packages**: Pip-installed packages that depend on `cadence_sdk`
- **Directory paths**: File system directories specified in `CADENCE_PLUGINS_DIR`
- **Custom registries**: Programmatic plugin registration via `register_plugin()`

**Auto-registration**: When a plugin package is imported, it automatically calls `register_plugin()` to make itself
available to the framework.

## Advanced Usage

### Custom Tool Decorators

```python
from cadence_sdk import tool


@tool
def weather_tool(city: str) -> str:
    """Get weather information for a city."""
    # Implementation here
    return f"Weather for {city}: Sunny, 72°F"


# Tools are automatically registered when using the decorator
weather_tools = [weather_tool]
```

### Parallel Tool Calls Support

BaseAgent supports parallel tool execution, allowing multiple tools to be called simultaneously for improved performance
and efficiency:

```python
from cadence_sdk import BaseAgent, PluginMetadata


class ParallelAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        # Enable parallel tool calls (default: True)
        super().__init__(metadata, parallel_tool_calls=True)

    def get_tools(self):
        return [tool1, tool2, tool3]

    def get_system_prompt(self) -> str:
        return "You are an agent that can execute multiple tools in parallel."


class SequentialAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        # Disable parallel tool calls for sequential execution
        super().__init__(metadata, parallel_tool_calls=False)

    def get_tools(self):
        return [tool1, tool2, tool3]

    def get_system_prompt(self) -> str:
        return "You are an agent that executes tools sequentially."
```

**Benefits of Parallel Tool Calls:**

- **Improved Performance**: Multiple tools execute concurrently instead of sequentially
- **Better User Experience**: Faster response times for multi-step operations
- **Resource Optimization**: Efficient use of computational resources
- **Scalability**: Better handling of complex, multi-tool workflows

**When to Use Parallel Tool Calls:**

- ✅ **Enable** when tools are independent and can run concurrently
- ✅ **Enable** for performance-critical operations
- ✅ **Enable** for I/O-bound operations (API calls, database queries, file operations)
- ✅ **Disable** when tools have dependencies or shared resources
- ✅ **Disable** when tools modify shared state sequentially
- ✅ **Disable** for debugging and troubleshooting

### Agent State Management

```python
from cadence_sdk import BaseAgent, PluginMetadata


class StatefulAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        # Enable parallel tool calls (default behavior)
        super().__init__(metadata, parallel_tool_calls=True)

    def get_tools(self):
        return []

    def get_system_prompt(self) -> str:
        return "You are a stateful agent that maintains context."

    @staticmethod
    def should_continue(state: dict) -> str:
        """Enhanced routing decision - decide whether to continue or return to coordinator.

        This is the REAL implementation from the Cadence SDK - it's much simpler than you might expect!
        The method simply checks if the agent's response has tool calls and routes accordingly.
        """
        last_msg = state.get("messages", [])[-1] if state.get("messages") else None
        if not last_msg:
            return "back"

        tool_calls = getattr(last_msg, "tool_calls", None)
        return "continue" if tool_calls else "back"
```

**Enhanced Routing System**: The `should_continue` method allows agents to control workflow flow by returning:

- `"continue"`: Keep processing with current agent (has tool calls)
- `"back"`: Return control to the coordinator (no tool calls)

**Key Benefits:**

- **Intelligent Decision Making**: Agents automatically decide routing based on their responses
- **Consistent Flow**: All responses go through the same routing path
- **No Circular Routing**: Eliminated infinite loops through proper edge configuration
- **Better Debugging**: Clear routing decisions and comprehensive logging
- **Predictable Behavior**: System behavior is more predictable and maintainable

### Plugin Registry

```python
from cadence_sdk import PluginRegistry

# Get plugin registry
registry = PluginRegistry()

# Register custom plugin
registry.register(CalculatorPlugin())

# Discover plugins
plugins = registry.discover()

# Get specific plugin
calculator_plugin = registry.get_plugin("calculator")
```

**Registry Features**: The plugin registry provides:

- Automatic plugin discovery and loading
- Plugin validation and health checks
- Metadata access and plugin management
- Integration with the main Cadence framework

## Examples

### Math Agent

```python
from cadence_sdk import BaseAgent, PluginMetadata, tool


class MathAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        # Enable parallel tool calls for concurrent calculations
        super().__init__(metadata, parallel_tool_calls=True)

    def get_tools(self):
        from .tools import math_tools
        return math_tools

    def get_system_prompt(self) -> str:
        return "You are a math agent specialized in mathematical operations. Use the calculator tool for calculations."

    @staticmethod
    def should_continue(state: dict) -> str:
        """Enhanced routing decision - decide whether to continue or return to coordinator."""
        last_msg = state.get("messages", [])[-1] if state.get("messages") else None
        if not last_msg:
            return "back"

        tool_calls = getattr(last_msg, "tool_calls", None)
        return "continue" if tool_calls else "back"


@tool
def calculate(expression: str) -> str:
    """Perform mathematical calculations"""
    try:
        result = eval(expression)
        return f"Result: {result}"
    except Exception as e:
        return f"Invalid expression: {str(e)}"


@tool
def add(a: int, b: int) -> int:
    """Add two numbers together"""
    return a + b


math_tools = [calculate, add]
```

### Search Agent

```python
from cadence_sdk import BaseAgent, PluginMetadata, tool
import requests


class SearchAgent(BaseAgent):
    def __init__(self, metadata: PluginMetadata):
        # Enable parallel tool calls for concurrent search operations
        super().__init__(metadata, parallel_tool_calls=True)

    def get_tools(self):
        from .tools import search_tools
        return search_tools

    def get_system_prompt(self) -> str:
        return "You are a search agent that helps users find information on the web. Use the web search tool to perform searches."

    @staticmethod
    def should_continue(state: dict) -> str:
        """Enhanced routing decision - decide whether to continue or return to coordinator."""
        last_msg = state.get("messages", [])[-1] if state.get("messages") else None
        if not last_msg:
            return "back"

        tool_calls = getattr(last_msg, "tool_calls", None)
        return "continue" if tool_calls else "back"


@tool
def web_search(query: str) -> str:
    """Search the web for information"""
    # Implementation would go here
    return f"Searching for: {query}"


@tool
def news_search(topic: str) -> str:
    """Search for news about a specific topic"""
    # Implementation would go here
    return f"Searching for news about: {topic}"


search_tools = [web_search, news_search]
```

## Best Practices

### Plugin Design Guidelines

1. **Single Responsibility**: Each plugin should focus on one specific domain or capability
2. **Clear Naming**: Use descriptive names for plugins, agents, and tools
3. **Proper Error Handling**: Always handle exceptions in tool functions
4. **Documentation**: Provide clear docstrings for all tools and methods
5. **Type Hints**: Use proper type annotations for better code quality
6. **Testing**: Include unit tests for your tools and agent logic
7. **Enhanced Routing**: Implement the `should_continue` method for intelligent routing decisions
8. **Consistent Flow**: Use fake tool calls when agents answer directly to maintain routing consistency
9. **Parallel Tool Calls**: Configure `parallel_tool_calls` parameter based on your tools' execution requirements

### Enhanced Routing Best Practices

```python
class EnhancedAgent(BaseAgent):
    @staticmethod
    def should_continue(state: dict) -> str:
        """Implement intelligent routing decisions based on agent response.

        This is the REAL implementation from the Cadence SDK - it's much simpler than you might expect!
        The method simply checks if the agent's response has tool calls and routes accordingly.
        """
        last_msg = state.get("messages", [])[-1] if state.get("messages") else None
        if not last_msg:
            return "back"

        tool_calls = getattr(last_msg, "tool_calls", None)
        return "continue" if tool_calls else "back"
```

**Important Implementation Notes:**

- **`should_continue` must be a static method**: Use `@staticmethod` decorator
- **The SDK automatically handles fake tool calls**: When agents answer directly, fake "back" tool calls are created
  automatically
- **No manual fake tool call creation needed**: The system handles this transparently

**Routing Guidelines:**

- **Always implement `should_continue`**: This method controls the conversation flow
- **Return "continue" for tool calls**: When agent generates tool calls, route to tools
- **Return "back" for direct answers**: When agent answers directly, return to coordinator
- **Use fake tool calls**: The system automatically creates fake "back" tool calls for consistency
- **Test both scenarios**: Ensure your agent works with and without tool calls

### Common Patterns

```python
# Tool function with proper error handling
@tool
def safe_operation(input_data: str) -> str:
    """Perform a safe operation with error handling."""
    try:
        # Your logic here
        result = process_data(input_data)
        return f"Success: {result}"
    except Exception as e:
        return f"Error: {str(e)}"

# Agent with comprehensive tool collection
class ComprehensiveAgent(BaseAgent):
    def get_tools(self):
        from .tools import (
            primary_tools,
            utility_tools,
            validation_tools
        )
        return primary_tools + utility_tools + validation_tools

    def get_system_prompt(self) -> str:
        return (
            "You are a comprehensive agent with multiple capabilities. "
            "Use the appropriate tools based on the user's request. "
            "Always explain your reasoning and show your work."
        )
```

## Development

### Setting up Development Environment

```bash
# Clone the main repository
git clone https://github.com/jonaskahn/cadence.git
cd cadence

# Install SDK dependencies
cd sdk
poetry install

# Run tests
poetry run pytest

# Format code
poetry run black src/
poetry run isort src/
```

### Testing

```bash
# Run all tests
poetry run pytest

# Run with coverage
poetry run pytest --cov=src/cadence_sdk

# Run specific test categories
poetry run pytest -m "unit"
poetry run pytest -m "integration"
```

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

### Development Setup

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Troubleshooting

### Common Issues

1. **Plugin Not Loading**: Ensure `register_plugin()` is called in `__init__.py`
2. **Import Errors**: Check that `cadence_sdk` is properly installed and imported
3. **Tool Registration**: Verify tools are decorated with `@tool` and included in the tools list
4. **Metadata Issues**: Ensure all required fields are provided in `PluginMetadata`

### Debug Tips

```python
# Enable debug logging
import logging
logging.basicConfig(level=logging.DEBUG)

# Check plugin registration
from cadence_sdk import discover_plugins
plugins = discover_plugins()
print(f"Discovered plugins: {[p.name for p in plugins]}")

# Verify tool decoration
from .tools import my_tool
print(f"Tool type: {type(my_tool)}")
print(f"Tool name: {getattr(my_tool, 'name', 'No name')}")
```

## Support

- **Documentation**: [Read the Docs](https://cadence.readthedocs.io/)
- **Issues**: [GitHub Issues](https://github.com/jonaskahn/cadence/issues)
- **Discussions**: [GitHub Discussions](https://github.com/jonaskahn/cadence/discussions)

## Quick Reference

### Essential Imports

```python
from cadence_sdk import BaseAgent, BasePlugin, PluginMetadata, tool, register_plugin
```

### Required Methods

- **Plugin**: `get_metadata()`, `create_agent()`
- **Agent**: `get_tools()`, `get_system_prompt()`
- **Tools**: Use `@tool` decorator

### File Structure

```
my_plugin/
├── __init__.py          # register_plugin(MyPlugin)
├── plugin.py            # BasePlugin implementation
├── agent.py             # BaseAgent implementation
└── tools.py             # @tool decorated functions
```

### Environment Variables

```bash
export CADENCE_PLUGINS_DIR="./plugins"
export CADENCE_DEFAULT_LLM_PROVIDER=openai
export CADENCE_OPENAI_API_KEY=your-key
```

---

**Built with ❤️ for the Cadence AI community**

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/jonaskahn/cadence-sdk",
    "name": "cadence-sdk",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.14,>=3.13",
    "maintainer_email": null,
    "keywords": "ai, agents, cadence-ai, cadence_sdk, langchain, langgraph, plugins",
    "author": "Jonas Kahn",
    "author_email": "me@ifelse.one",
    "download_url": "https://files.pythonhosted.org/packages/25/a6/df450a392ec3a7ed3d3094bc220bccf7d95b44a0b30f4d77b4e5dc4b3b00/cadence_sdk-1.0.7.tar.gz",
    "platform": null,
    "description": "# Cadence SDK\n\nA comprehensive SDK for building custom AI agent plugins for the Cadence Framework.\n\n## Overview\n\nThe Cadence SDK provides the tools and interfaces needed to create powerful, extensible AI agents that integrate\nseamlessly with the Cadence multi-agent framework. Build agents with custom tools, sophisticated reasoning capabilities,\nand domain-specific knowledge.\n\n## Features\n\n- **Agent Framework**: Create intelligent agents with custom behavior and system prompts\n- **Tool System**: Build and integrate custom tools using the `@tool` decorator\n- **Plugin Management**: Easy plugin discovery and registration with automatic loading\n- **Type Safety**: Full Python type support with proper annotations\n- **Extensible**: Plugin-based architecture for easy extension and customization\n- **LangGraph Integration**: Seamless integration with LangGraph workflows\n- **LLM Binding**: Automatic tool binding to language models\n\n## Installation\n\n```bash\npip install cadence-sdk\n```\n\n## Quick Start\n\n### Key Imports\n\n```python\n# Core classes - import from main SDK module (recommended)\nfrom cadence_sdk import BaseAgent, BasePlugin, PluginMetadata, tool, register_plugin\n\n# Alternative: import specific components if needed\nfrom cadence_sdk.base.agent import BaseAgent\nfrom cadence_sdk.base.plugin import BasePlugin\nfrom cadence_sdk.base.metadata import PluginMetadata\nfrom cadence_sdk.tools.decorators import tool\nfrom cadence_sdk import register_plugin, discover_plugins\n```\n\n**Note**: The main import approach is recommended for most use cases as it provides all necessary components in one\nimport statement.\n\n### Creating a Simple Agent\n\n```python\nfrom cadence_sdk import BaseAgent, PluginMetadata, tool\n\n\nclass CalculatorAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        super().__init__(metadata)\n\n    def get_tools(self):\n        from .tools import math_tools\n        return math_tools\n\n    def get_system_prompt(self) -> str:\n        return \"You are a calculator agent that helps with mathematical calculations.\"\n\n\n@tool\ndef calculate(expression: str) -> str:\n    \"\"\"Perform mathematical calculations\"\"\"\n    try:\n        result = eval(expression)\n        return str(result)\n    except Exception as e:\n        return f\"Error: {str(e)}\"\n```\n\n### Plugin Structure\n\n```\nmy_plugin/\n\u251c\u2500\u2500 __init__.py          # Plugin registration with register_plugin()\n\u251c\u2500\u2500 plugin.py            # Main plugin class (BasePlugin)\n\u251c\u2500\u2500 agent.py             # Agent implementation (BaseAgent)\n\u251c\u2500\u2500 tools.py             # Tool functions with @tool decorator\n\u251c\u2500\u2500 pyproject.toml       # Package configuration\n\u2514\u2500\u2500 README.md            # Documentation\n```\n\n**Required Files:**\n\n- `__init__.py`: Must call `register_plugin(YourPlugin)` to auto-register the plugin\n- `plugin.py`: Must implement `BasePlugin` with `get_metadata()` and `create_agent()` methods\n- `agent.py`: Must implement `BaseAgent` with `get_tools()` and `get_system_prompt()` methods\n- `tools.py`: Contains tool functions decorated with `@tool` decorator\n- `pyproject.toml`: Package metadata and dependencies\n\n### Plugin Registration\n\n```python\nfrom cadence_sdk import BasePlugin, PluginMetadata\n\n\nclass CalculatorPlugin(BasePlugin):\n    @staticmethod\n    def get_metadata() -> PluginMetadata:\n        return PluginMetadata(\n            name=\"calculator\",\n            version=\"1.0.7\",\n            description=\"Mathematical calculation plugin\",\n            capabilities=[\"mathematics\", \"calculations\"],\n            llm_requirements={\n                \"provider\": \"openai\",\n                \"model\": \"gpt-4\",\n                \"temperature\": 0.1\n            },\n            agent_type=\"specialized\",\n            dependencies=[\"cadence_sdk>=1.0.2,<2.0.0\"]\n        )\n\n    @staticmethod\n    def create_agent():\n        from .agent import CalculatorAgent\n        return CalculatorAgent(CalculatorPlugin.get_metadata())\n```\n\n## Configuration\n\n### Plugin Registration\n\nTo make your plugin discoverable by the Cadence framework, you need to register it in your plugin's `__init__.py`:\n\n```python\n# plugins/src/cadence_example_plugins/my_plugin/__init__.py\nfrom cadence_sdk import register_plugin\nfrom .plugin import MyPlugin\n\n# Register on import\nregister_plugin(MyPlugin)\n```\n\n### Environment Variables\n\n```bash\n# Set plugin directories (single path)\nexport CADENCE_PLUGINS_DIR=\"./plugins/src/cadence_plugins\"\n\n# Or multiple directories as JSON array\nexport CADENCE_PLUGINS_DIR='[\"/path/to/plugins\", \"/another/path\"]'\n\n# Plugin limits (configured in main application)\nexport CADENCE_MAX_AGENT_HOPS=25\n\nexport CADENCE_GRAPH_RECURSION_LIMIT=50\n\n# LLM Provider Configuration\nexport CADENCE_DEFAULT_LLM_PROVIDER=openai\nexport CADENCE_OPENAI_API_KEY=your-api-key\n```\n\n### Plugin Discovery\n\nThe SDK automatically discovers plugins from:\n\n- **Environment packages**: Pip-installed packages that depend on `cadence_sdk`\n- **Directory paths**: File system directories specified in `CADENCE_PLUGINS_DIR`\n- **Custom registries**: Programmatic plugin registration via `register_plugin()`\n\n**Auto-registration**: When a plugin package is imported, it automatically calls `register_plugin()` to make itself\navailable to the framework.\n\n## Advanced Usage\n\n### Custom Tool Decorators\n\n```python\nfrom cadence_sdk import tool\n\n\n@tool\ndef weather_tool(city: str) -> str:\n    \"\"\"Get weather information for a city.\"\"\"\n    # Implementation here\n    return f\"Weather for {city}: Sunny, 72\u00b0F\"\n\n\n# Tools are automatically registered when using the decorator\nweather_tools = [weather_tool]\n```\n\n### Parallel Tool Calls Support\n\nBaseAgent supports parallel tool execution, allowing multiple tools to be called simultaneously for improved performance\nand efficiency:\n\n```python\nfrom cadence_sdk import BaseAgent, PluginMetadata\n\n\nclass ParallelAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        # Enable parallel tool calls (default: True)\n        super().__init__(metadata, parallel_tool_calls=True)\n\n    def get_tools(self):\n        return [tool1, tool2, tool3]\n\n    def get_system_prompt(self) -> str:\n        return \"You are an agent that can execute multiple tools in parallel.\"\n\n\nclass SequentialAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        # Disable parallel tool calls for sequential execution\n        super().__init__(metadata, parallel_tool_calls=False)\n\n    def get_tools(self):\n        return [tool1, tool2, tool3]\n\n    def get_system_prompt(self) -> str:\n        return \"You are an agent that executes tools sequentially.\"\n```\n\n**Benefits of Parallel Tool Calls:**\n\n- **Improved Performance**: Multiple tools execute concurrently instead of sequentially\n- **Better User Experience**: Faster response times for multi-step operations\n- **Resource Optimization**: Efficient use of computational resources\n- **Scalability**: Better handling of complex, multi-tool workflows\n\n**When to Use Parallel Tool Calls:**\n\n- \u2705 **Enable** when tools are independent and can run concurrently\n- \u2705 **Enable** for performance-critical operations\n- \u2705 **Enable** for I/O-bound operations (API calls, database queries, file operations)\n- \u2705 **Disable** when tools have dependencies or shared resources\n- \u2705 **Disable** when tools modify shared state sequentially\n- \u2705 **Disable** for debugging and troubleshooting\n\n### Agent State Management\n\n```python\nfrom cadence_sdk import BaseAgent, PluginMetadata\n\n\nclass StatefulAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        # Enable parallel tool calls (default behavior)\n        super().__init__(metadata, parallel_tool_calls=True)\n\n    def get_tools(self):\n        return []\n\n    def get_system_prompt(self) -> str:\n        return \"You are a stateful agent that maintains context.\"\n\n    @staticmethod\n    def should_continue(state: dict) -> str:\n        \"\"\"Enhanced routing decision - decide whether to continue or return to coordinator.\n\n        This is the REAL implementation from the Cadence SDK - it's much simpler than you might expect!\n        The method simply checks if the agent's response has tool calls and routes accordingly.\n        \"\"\"\n        last_msg = state.get(\"messages\", [])[-1] if state.get(\"messages\") else None\n        if not last_msg:\n            return \"back\"\n\n        tool_calls = getattr(last_msg, \"tool_calls\", None)\n        return \"continue\" if tool_calls else \"back\"\n```\n\n**Enhanced Routing System**: The `should_continue` method allows agents to control workflow flow by returning:\n\n- `\"continue\"`: Keep processing with current agent (has tool calls)\n- `\"back\"`: Return control to the coordinator (no tool calls)\n\n**Key Benefits:**\n\n- **Intelligent Decision Making**: Agents automatically decide routing based on their responses\n- **Consistent Flow**: All responses go through the same routing path\n- **No Circular Routing**: Eliminated infinite loops through proper edge configuration\n- **Better Debugging**: Clear routing decisions and comprehensive logging\n- **Predictable Behavior**: System behavior is more predictable and maintainable\n\n### Plugin Registry\n\n```python\nfrom cadence_sdk import PluginRegistry\n\n# Get plugin registry\nregistry = PluginRegistry()\n\n# Register custom plugin\nregistry.register(CalculatorPlugin())\n\n# Discover plugins\nplugins = registry.discover()\n\n# Get specific plugin\ncalculator_plugin = registry.get_plugin(\"calculator\")\n```\n\n**Registry Features**: The plugin registry provides:\n\n- Automatic plugin discovery and loading\n- Plugin validation and health checks\n- Metadata access and plugin management\n- Integration with the main Cadence framework\n\n## Examples\n\n### Math Agent\n\n```python\nfrom cadence_sdk import BaseAgent, PluginMetadata, tool\n\n\nclass MathAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        # Enable parallel tool calls for concurrent calculations\n        super().__init__(metadata, parallel_tool_calls=True)\n\n    def get_tools(self):\n        from .tools import math_tools\n        return math_tools\n\n    def get_system_prompt(self) -> str:\n        return \"You are a math agent specialized in mathematical operations. Use the calculator tool for calculations.\"\n\n    @staticmethod\n    def should_continue(state: dict) -> str:\n        \"\"\"Enhanced routing decision - decide whether to continue or return to coordinator.\"\"\"\n        last_msg = state.get(\"messages\", [])[-1] if state.get(\"messages\") else None\n        if not last_msg:\n            return \"back\"\n\n        tool_calls = getattr(last_msg, \"tool_calls\", None)\n        return \"continue\" if tool_calls else \"back\"\n\n\n@tool\ndef calculate(expression: str) -> str:\n    \"\"\"Perform mathematical calculations\"\"\"\n    try:\n        result = eval(expression)\n        return f\"Result: {result}\"\n    except Exception as e:\n        return f\"Invalid expression: {str(e)}\"\n\n\n@tool\ndef add(a: int, b: int) -> int:\n    \"\"\"Add two numbers together\"\"\"\n    return a + b\n\n\nmath_tools = [calculate, add]\n```\n\n### Search Agent\n\n```python\nfrom cadence_sdk import BaseAgent, PluginMetadata, tool\nimport requests\n\n\nclass SearchAgent(BaseAgent):\n    def __init__(self, metadata: PluginMetadata):\n        # Enable parallel tool calls for concurrent search operations\n        super().__init__(metadata, parallel_tool_calls=True)\n\n    def get_tools(self):\n        from .tools import search_tools\n        return search_tools\n\n    def get_system_prompt(self) -> str:\n        return \"You are a search agent that helps users find information on the web. Use the web search tool to perform searches.\"\n\n    @staticmethod\n    def should_continue(state: dict) -> str:\n        \"\"\"Enhanced routing decision - decide whether to continue or return to coordinator.\"\"\"\n        last_msg = state.get(\"messages\", [])[-1] if state.get(\"messages\") else None\n        if not last_msg:\n            return \"back\"\n\n        tool_calls = getattr(last_msg, \"tool_calls\", None)\n        return \"continue\" if tool_calls else \"back\"\n\n\n@tool\ndef web_search(query: str) -> str:\n    \"\"\"Search the web for information\"\"\"\n    # Implementation would go here\n    return f\"Searching for: {query}\"\n\n\n@tool\ndef news_search(topic: str) -> str:\n    \"\"\"Search for news about a specific topic\"\"\"\n    # Implementation would go here\n    return f\"Searching for news about: {topic}\"\n\n\nsearch_tools = [web_search, news_search]\n```\n\n## Best Practices\n\n### Plugin Design Guidelines\n\n1. **Single Responsibility**: Each plugin should focus on one specific domain or capability\n2. **Clear Naming**: Use descriptive names for plugins, agents, and tools\n3. **Proper Error Handling**: Always handle exceptions in tool functions\n4. **Documentation**: Provide clear docstrings for all tools and methods\n5. **Type Hints**: Use proper type annotations for better code quality\n6. **Testing**: Include unit tests for your tools and agent logic\n7. **Enhanced Routing**: Implement the `should_continue` method for intelligent routing decisions\n8. **Consistent Flow**: Use fake tool calls when agents answer directly to maintain routing consistency\n9. **Parallel Tool Calls**: Configure `parallel_tool_calls` parameter based on your tools' execution requirements\n\n### Enhanced Routing Best Practices\n\n```python\nclass EnhancedAgent(BaseAgent):\n    @staticmethod\n    def should_continue(state: dict) -> str:\n        \"\"\"Implement intelligent routing decisions based on agent response.\n\n        This is the REAL implementation from the Cadence SDK - it's much simpler than you might expect!\n        The method simply checks if the agent's response has tool calls and routes accordingly.\n        \"\"\"\n        last_msg = state.get(\"messages\", [])[-1] if state.get(\"messages\") else None\n        if not last_msg:\n            return \"back\"\n\n        tool_calls = getattr(last_msg, \"tool_calls\", None)\n        return \"continue\" if tool_calls else \"back\"\n```\n\n**Important Implementation Notes:**\n\n- **`should_continue` must be a static method**: Use `@staticmethod` decorator\n- **The SDK automatically handles fake tool calls**: When agents answer directly, fake \"back\" tool calls are created\n  automatically\n- **No manual fake tool call creation needed**: The system handles this transparently\n\n**Routing Guidelines:**\n\n- **Always implement `should_continue`**: This method controls the conversation flow\n- **Return \"continue\" for tool calls**: When agent generates tool calls, route to tools\n- **Return \"back\" for direct answers**: When agent answers directly, return to coordinator\n- **Use fake tool calls**: The system automatically creates fake \"back\" tool calls for consistency\n- **Test both scenarios**: Ensure your agent works with and without tool calls\n\n### Common Patterns\n\n```python\n# Tool function with proper error handling\n@tool\ndef safe_operation(input_data: str) -> str:\n    \"\"\"Perform a safe operation with error handling.\"\"\"\n    try:\n        # Your logic here\n        result = process_data(input_data)\n        return f\"Success: {result}\"\n    except Exception as e:\n        return f\"Error: {str(e)}\"\n\n# Agent with comprehensive tool collection\nclass ComprehensiveAgent(BaseAgent):\n    def get_tools(self):\n        from .tools import (\n            primary_tools,\n            utility_tools,\n            validation_tools\n        )\n        return primary_tools + utility_tools + validation_tools\n\n    def get_system_prompt(self) -> str:\n        return (\n            \"You are a comprehensive agent with multiple capabilities. \"\n            \"Use the appropriate tools based on the user's request. \"\n            \"Always explain your reasoning and show your work.\"\n        )\n```\n\n## Development\n\n### Setting up Development Environment\n\n```bash\n# Clone the main repository\ngit clone https://github.com/jonaskahn/cadence.git\ncd cadence\n\n# Install SDK dependencies\ncd sdk\npoetry install\n\n# Run tests\npoetry run pytest\n\n# Format code\npoetry run black src/\npoetry run isort src/\n```\n\n### Testing\n\n```bash\n# Run all tests\npoetry run pytest\n\n# Run with coverage\npoetry run pytest --cov=src/cadence_sdk\n\n# Run specific test categories\npoetry run pytest -m \"unit\"\npoetry run pytest -m \"integration\"\n```\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests\n5. Submit a pull request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Plugin Not Loading**: Ensure `register_plugin()` is called in `__init__.py`\n2. **Import Errors**: Check that `cadence_sdk` is properly installed and imported\n3. **Tool Registration**: Verify tools are decorated with `@tool` and included in the tools list\n4. **Metadata Issues**: Ensure all required fields are provided in `PluginMetadata`\n\n### Debug Tips\n\n```python\n# Enable debug logging\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\n# Check plugin registration\nfrom cadence_sdk import discover_plugins\nplugins = discover_plugins()\nprint(f\"Discovered plugins: {[p.name for p in plugins]}\")\n\n# Verify tool decoration\nfrom .tools import my_tool\nprint(f\"Tool type: {type(my_tool)}\")\nprint(f\"Tool name: {getattr(my_tool, 'name', 'No name')}\")\n```\n\n## Support\n\n- **Documentation**: [Read the Docs](https://cadence.readthedocs.io/)\n- **Issues**: [GitHub Issues](https://github.com/jonaskahn/cadence/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/jonaskahn/cadence/discussions)\n\n## Quick Reference\n\n### Essential Imports\n\n```python\nfrom cadence_sdk import BaseAgent, BasePlugin, PluginMetadata, tool, register_plugin\n```\n\n### Required Methods\n\n- **Plugin**: `get_metadata()`, `create_agent()`\n- **Agent**: `get_tools()`, `get_system_prompt()`\n- **Tools**: Use `@tool` decorator\n\n### File Structure\n\n```\nmy_plugin/\n\u251c\u2500\u2500 __init__.py          # register_plugin(MyPlugin)\n\u251c\u2500\u2500 plugin.py            # BasePlugin implementation\n\u251c\u2500\u2500 agent.py             # BaseAgent implementation\n\u2514\u2500\u2500 tools.py             # @tool decorated functions\n```\n\n### Environment Variables\n\n```bash\nexport CADENCE_PLUGINS_DIR=\"./plugins\"\nexport CADENCE_DEFAULT_LLM_PROVIDER=openai\nexport CADENCE_OPENAI_API_KEY=your-key\n```\n\n---\n\n**Built with \u2764\ufe0f for the Cadence AI community**\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Cadence SDK - To building custom AI agent plugins for Cadence AI Framework",
    "version": "1.0.7",
    "project_urls": {
        "Documentation": "https://cadence_sdk.readthedocs.io/",
        "Homepage": "https://github.com/jonaskahn/cadence-sdk",
        "Repository": "https://github.com/jonaskahn/cadence-sdk.git",
        "issues": "https://github.com/jonaskahn/cadence-sdk/issues"
    },
    "split_keywords": [
        "ai",
        " agents",
        " cadence-ai",
        " cadence_sdk",
        " langchain",
        " langgraph",
        " plugins"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1e19d337a0cc37870d9aae977359e3720de5dcc4620a655d212bf8ec6715c89a",
                "md5": "d6673fe62d9646bfbb14de1680ee07d7",
                "sha256": "cee817bca87cdce298c2432a55f6c30d7a0dd200c593b94989d62f74e2e211f8"
            },
            "downloads": -1,
            "filename": "cadence_sdk-1.0.7-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d6673fe62d9646bfbb14de1680ee07d7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.14,>=3.13",
            "size": 32857,
            "upload_time": "2025-09-03T09:24:33",
            "upload_time_iso_8601": "2025-09-03T09:24:33.524135Z",
            "url": "https://files.pythonhosted.org/packages/1e/19/d337a0cc37870d9aae977359e3720de5dcc4620a655d212bf8ec6715c89a/cadence_sdk-1.0.7-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "25a6df450a392ec3a7ed3d3094bc220bccf7d95b44a0b30f4d77b4e5dc4b3b00",
                "md5": "e4bd4f134b212322df2e12b8e728d863",
                "sha256": "cdccdf19b482ddb76506494ed9afab9e12ab20ef3e6abb1c61a6a6574b29787d"
            },
            "downloads": -1,
            "filename": "cadence_sdk-1.0.7.tar.gz",
            "has_sig": false,
            "md5_digest": "e4bd4f134b212322df2e12b8e728d863",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.14,>=3.13",
            "size": 28675,
            "upload_time": "2025-09-03T09:24:34",
            "upload_time_iso_8601": "2025-09-03T09:24:34.648538Z",
            "url": "https://files.pythonhosted.org/packages/25/a6/df450a392ec3a7ed3d3094bc220bccf7d95b44a0b30f4d77b4e5dc4b3b00/cadence_sdk-1.0.7.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-03 09:24:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jonaskahn",
    "github_project": "cadence-sdk",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "cadence-sdk"
}
        
Elapsed time: 0.98860s