{"id":30716365,"url":"https://github.com/jonaskahn/cadence-sdk","last_synced_at":"2025-09-06T19:05:14.896Z","repository":{"id":312600155,"uuid":"1047996314","full_name":"jonaskahn/cadence-sdk","owner":"jonaskahn","description":"Cadence SDK - To building custom agent plugins for Cadence Framework","archived":false,"fork":false,"pushed_at":"2025-09-04T08:21:21.000Z","size":75,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-04T15:03:00.559Z","etag":null,"topics":["ai-agents-framework","cadence-framework","langraph","sdk"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/cadence-sdk/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonaskahn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-31T17:32:02.000Z","updated_at":"2025-09-04T08:21:24.000Z","dependencies_parsed_at":"2025-09-04T15:03:00.520Z","dependency_job_id":null,"html_url":"https://github.com/jonaskahn/cadence-sdk","commit_stats":null,"previous_names":["jonaskahn/cadence-sdk"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/jonaskahn/cadence-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonaskahn%2Fcadence-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonaskahn%2Fcadence-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonaskahn%2Fcadence-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonaskahn%2Fcadence-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonaskahn","download_url":"https://codeload.github.com/jonaskahn/cadence-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonaskahn%2Fcadence-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273657162,"owners_count":25145003,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents-framework","cadence-framework","langraph","sdk"],"created_at":"2025-09-03T08:01:19.171Z","updated_at":"2025-09-05T16:02:45.198Z","avatar_url":"https://github.com/jonaskahn.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 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) -\u003e str:\n        return \"You are a calculator agent that helps with mathematical calculations.\"\n\n\n@tool\ndef calculate(expression: str) -\u003e 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├── __init__.py          # Plugin registration with register_plugin()\n├── plugin.py            # Main plugin class (BasePlugin)\n├── agent.py             # Agent implementation (BaseAgent)\n├── tools.py             # Tool functions with @tool decorator\n├── pyproject.toml       # Package configuration\n└── 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() -\u003e PluginMetadata:\n        return PluginMetadata(\n            name=\"calculator\",\n            version=\"1.0.6\",\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\u003e=1.0.2,\u003c2.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) -\u003e str:\n    \"\"\"Get weather information for a city.\"\"\"\n    # Implementation here\n    return f\"Weather for {city}: Sunny, 72°F\"\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) -\u003e 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) -\u003e 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- ✅ **Enable** when tools are independent and can run concurrently\n- ✅ **Enable** for performance-critical operations\n- ✅ **Enable** for I/O-bound operations (API calls, database queries, file operations)\n- ✅ **Disable** when tools have dependencies or shared resources\n- ✅ **Disable** when tools modify shared state sequentially\n- ✅ **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) -\u003e str:\n        return \"You are a stateful agent that maintains context.\"\n\n    @staticmethod\n    def should_continue(state: dict) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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) -\u003e 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├── __init__.py          # register_plugin(MyPlugin)\n├── plugin.py            # BasePlugin implementation\n├── agent.py             # BaseAgent implementation\n└── 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 ❤️ for the Cadence AI community**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonaskahn%2Fcadence-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonaskahn%2Fcadence-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonaskahn%2Fcadence-sdk/lists"}