Skip to content

Conversation

@spumer
Copy link

@spumer spumer commented Nov 16, 2025

Add support for Claude Agent SDK as a new LLM provider, enabling the use of local Claude Code without API keys.

Key changes:

  • Created ClaudeAgentSDKModel wrapper implementing Agno Model interface
  • Implemented aresponse() and aresponse_stream() methods using claude_agent_sdk.query()
  • Added ClaudeAgentSDKStrategy to provider configuration system
  • Registered new provider in ConfigurationManager
  • Added claude-agent-sdk to project dependencies
  • Updated documentation (CLAUDE.md, README.md) with new provider details

Benefits:

  • No API key required - uses locally installed Claude Code
  • Seamless integration with existing Multi-Thinking architecture
  • Full support for all thinking agents and workflows
  • Native Agno framework compatibility

Provider usage:

LLM_PROVIDER="claude-agent-sdk"
# No additional configuration needed

Add support for Claude Agent SDK as a new LLM provider, enabling
the use of local Claude Code without API keys.

Key changes:
- Created ClaudeAgentSDKModel wrapper implementing Agno Model interface
- Implemented aresponse() and aresponse_stream() methods using claude_agent_sdk.query()
- Added ClaudeAgentSDKStrategy to provider configuration system
- Registered new provider in ConfigurationManager
- Added claude-agent-sdk to project dependencies
- Updated documentation (CLAUDE.md, README.md) with new provider details

Benefits:
- No API key required - uses locally installed Claude Code
- Seamless integration with existing Multi-Thinking architecture
- Full support for all thinking agents and workflows
- Native Agno framework compatibility

Provider usage:
```bash
LLM_PROVIDER="claude-agent-sdk"
# No additional configuration needed
```
@gemini-code-assist
Copy link

Summary of Changes

Hello @spumer, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement by integrating the Claude Agent SDK as a new Large Language Model (LLM) provider. This integration allows the system to leverage locally installed Claude Code, eliminating the need for external API keys and offering a more self-contained and potentially more secure operational mode for LLM interactions within the Multi-Thinking architecture.

Highlights

  • Claude Agent SDK Integration: Added support for Claude Agent SDK as a new LLM provider, enabling the use of local Claude Code.
  • API Key Free Operation: The new integration allows for model inference without requiring API keys, utilizing locally installed Claude Code.
  • Agno Framework Compatibility: Implemented a ClaudeAgentSDKModel wrapper that adheres to the Agno Model interface, including asynchronous aresponse() and aresponse_stream() methods.
  • Configuration System Update: Integrated ClaudeAgentSDKStrategy into the provider configuration system and registered it within the ConfigurationManager.
  • Dependency Management: Added claude-agent-sdk to the project's dependencies in pyproject.toml and updated uv.lock accordingly.
  • Documentation Updates: Updated CLAUDE.md and README.md to reflect the new provider, its usage, and the absence of API key requirements.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively integrates the Claude Agent SDK as a new LLM provider. The changes are well-structured, including the new model wrapper, provider strategy, and updates to documentation and dependencies. The code is generally clean and follows good practices like lazy loading the SDK. My review includes suggestions to refactor duplicated code in the new model wrapper for better maintainability, improve robustness in message processing, and clarify a potentially unintended dependency update.

Comment on lines 82 to 94
for msg in messages:
role = msg.role if hasattr(msg, "role") else "user"
content = msg.content if hasattr(msg, "content") else str(msg)

# Format based on role
if role == "system":
prompt_parts.append(f"System: {content}")
elif role == "user":
prompt_parts.append(f"User: {content}")
elif role == "assistant":
prompt_parts.append(f"Assistant: {content}")
else:
prompt_parts.append(str(content))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of hasattr with ternary operators can be simplified using getattr with default values, making the code more concise. Additionally, the else block for an unknown role silently appends the content; it would be more robust to log a warning when an unexpected role is encountered. This helps in debugging and understanding the conversation flow.

Suggested change
for msg in messages:
role = msg.role if hasattr(msg, "role") else "user"
content = msg.content if hasattr(msg, "content") else str(msg)
# Format based on role
if role == "system":
prompt_parts.append(f"System: {content}")
elif role == "user":
prompt_parts.append(f"User: {content}")
elif role == "assistant":
prompt_parts.append(f"Assistant: {content}")
else:
prompt_parts.append(str(content))
for msg in messages:
role = getattr(msg, "role", "user")
content = getattr(msg, "content", str(msg))
# Format based on role
if role == "system":
prompt_parts.append(f"System: {content}")
elif role == "user":
prompt_parts.append(f"User: {content}")
elif role == "assistant":
prompt_parts.append(f"Assistant: {content}")
else:
logger.warning("Unknown message role '%s', appending content directly.", role)
prompt_parts.append(str(content))

Comment on lines +137 to +150
if hasattr(message, "content"):
# Handle different content formats
if isinstance(message.content, str):
full_response += message.content
elif isinstance(message.content, list):
# Handle content blocks
for block in message.content:
if hasattr(block, "text"):
full_response += block.text
elif isinstance(block, dict) and "text" in block:
full_response += block["text"]
else:
# Fallback: convert to string
full_response += str(message)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to extract content from the message object is duplicated here and in aresponse_stream (lines 216-226). To improve maintainability and avoid code duplication, consider refactoring this logic into a private helper method. For example:

def _extract_text_from_message(self, message: Any) -> str:
    # ... implementation ...

You could then call this helper method in both aresponse and aresponse_stream.

Comment on lines +166 to +167
except Exception as e:
logger.exception("Claude Agent SDK query failed")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Catching a broad Exception can mask unexpected issues and make debugging harder. It's better to catch more specific exceptions that _claude_query might raise, if the SDK provides them (e.g., APIError, ConnectionError). This allows for more targeted error handling and avoids accidentally catching exceptions like KeyboardInterrupt. This applies to the exception handling in aresponse_stream as well.

Improve Claude Agent SDK integration by properly using ClaudeAgentOptions:

- Import and use ClaudeAgentOptions for query configuration
- Extract system prompts from messages and pass via options.system_prompt
- Configure max_turns using tool_call_limit parameter
- Separate system messages from user/assistant messages
- Add proper logging for system prompt length

Benefits:
- Proper separation of system prompts (not inline in main prompt)
- Better control over conversation flow via max_turns
- More idiomatic usage of Claude Agent SDK API
- Improved debugging with system prompt visibility in logs
Implement comprehensive Claude Agent SDK integration improvements:

**1. Tool Management (allowed_tools)**
- Map Agno tools to Claude Agent SDK allowed_tools
- ReasoningTools → 'Think' tool
- ExaTools → 'search_exa' tool
- Support for Function objects and dict-based tools
- Intelligent tool name mapping

**2. Model Configuration**
- Pass model ID explicitly via options.model
- Ensures Claude Code uses correct model version

**3. Permission Mode**
- Add permission_mode parameter with 4 modes:
  * 'default': Standard permissions with prompts
  * 'acceptEdits': Auto-accept file edits
  * 'plan': Plan mode for reviewing actions
  * 'bypassPermissions': Bypass all checks (default for automation)
- Configurable per-instance for different use cases

**4. Working Directory (cwd)**
- Support custom working directory
- Defaults to current directory via Path.cwd()
- Enables context-aware file operations

**5. Tool Calls Extraction**
- Extract and parse tool_use blocks from responses
- Include tool calls in ModelResponse.tool_calls
- Support both object and dict-based tool blocks
- Track tool invocations (Think, search, etc.)

**Benefits:**
- Full control over Claude Code behavior
- Better tool integration with Agno framework
- Visibility into tool usage via tool_calls
- Context-aware operations via cwd
- Flexible permission management for different scenarios

**What's NOT included (as requested):**
- max_thinking_tokens (skipped)
- max_budget_usd (skipped)
Add comprehensive support for medium-priority Claude Agent SDK features:

**1. MCP Servers Integration (mcp_servers)**
- Support dict configuration, path to config file, or None
- Enables additional MCP servers for extended tool capabilities
- Integrated into ClaudeAgentOptions

**2. Environment Variables (env)**
- Pass custom environment variables to Claude Code
- Support tool-specific configuration via env vars
- Dict[str, str] format for key-value pairs

**3. Additional Directories (add_dirs)**
- Extend file access context with additional directories
- List[str | Path] format, converted to string paths
- Enables broader project context for agents

**4. Event Hooks (hooks)**
- Support for Claude Agent SDK event hooks:
  * PreToolUse: Before tool execution
  * PostToolUse: After tool execution
  * UserPromptSubmit: On user prompt submission
  * Stop: On agent stop
  * SubagentStop: On subagent stop
  * PreCompact: Before memory compaction
- Dict[str, List[Any]] format for hook matchers
- Enables event-driven integrations

**5. Tool Permission Callback (can_use_tool)**
- Runtime permission checks for tool usage
- Async callback: (tool_name, args, context) -> PermissionResult
- Enables fine-grained security control
- Integration with Agno permission system

**6. Enhanced Documentation**
- Comprehensive docstring with feature list
- Usage examples for each feature
- Clear parameter documentation
- Provider data tracking for all configurations

**Implementation Details:**
- Conditional parameter passing (only if provided)
- Type-safe with proper type hints
- Logging for all configuration options
- Provider data includes config status for debugging

**Benefits:**
- Full Claude Agent SDK feature parity
- Fine-grained control over agent behavior
- Event-driven architecture support
- Security through permission callbacks
- Extensible via MCP servers
- Environment-specific configurations
@spumer
Copy link
Author

spumer commented Nov 17, 2025

Sorry, Claude code web create PR to your project instead my fork )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants