Spaces:
Running
Running
Add missing import
Browse files- agents/MODAL_DEBUGGING.md +17 -12
- agents/agent.py +9 -2
agents/MODAL_DEBUGGING.md
CHANGED
|
@@ -37,34 +37,39 @@ os.environ["USE_CWD_AS_PROJECT_ROOT"] = "1"
|
|
| 37 |
PROJECT_ROOT = Path(os.getcwd()) if os.environ.get("USE_CWD_AS_PROJECT_ROOT") else SCRIPT_DIR.parent
|
| 38 |
```
|
| 39 |
|
| 40 |
-
### 4. ✅
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
- Removed `stderr` callback
|
| 45 |
-
- Removed `max_turns`
|
| 46 |
-
- Removed `extra_args={"debug-to-stderr": None}`
|
| 47 |
-
|
| 48 |
-
**Simplified to:**
|
| 49 |
```python
|
|
|
|
|
|
|
|
|
|
| 50 |
options = ClaudeAgentOptions(
|
| 51 |
system_prompt=system_prompt,
|
| 52 |
permission_mode="bypassPermissions",
|
| 53 |
settings=settings_path,
|
|
|
|
| 54 |
)
|
| 55 |
```
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
## Working Configuration
|
| 58 |
|
| 59 |
### modal_agent.py Key Points:
|
| 60 |
- Python 3.11 (matching working example)
|
| 61 |
- Correct sys.path order (mounted code takes priority)
|
| 62 |
- Set `USE_CWD_AS_PROJECT_ROOT=1` before importing agent
|
| 63 |
-
-
|
| 64 |
|
| 65 |
### agent.py Key Points:
|
| 66 |
-
-
|
| 67 |
- Dynamic `PROJECT_ROOT` based on environment variable
|
|
|
|
| 68 |
|
| 69 |
## Test Command
|
| 70 |
|
|
@@ -72,7 +77,7 @@ options = ClaudeAgentOptions(
|
|
| 72 |
uv run modal run agents/modal_agent.py --conference-name neurips
|
| 73 |
```
|
| 74 |
|
| 75 |
-
Expected: Multiple messages (
|
| 76 |
|
| 77 |
## Modal Secrets Required
|
| 78 |
|
|
|
|
| 37 |
PROJECT_ROOT = Path(os.getcwd()) if os.environ.get("USE_CWD_AS_PROJECT_ROOT") else SCRIPT_DIR.parent
|
| 38 |
```
|
| 39 |
|
| 40 |
+
### 4. ✅ stderr Callback REQUIRED (CRITICAL!)
|
| 41 |
+
**This was the key fix discovered on 2025-12-26:** The `stderr` callback MUST be provided for the SDK to work on Modal. Without it, the async generator only yields the SystemMessage and then exits immediately.
|
| 42 |
+
|
| 43 |
+
**Required configuration:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
```python
|
| 45 |
+
def on_stderr(data: str):
|
| 46 |
+
print(f"[stderr] {data.strip()}")
|
| 47 |
+
|
| 48 |
options = ClaudeAgentOptions(
|
| 49 |
system_prompt=system_prompt,
|
| 50 |
permission_mode="bypassPermissions",
|
| 51 |
settings=settings_path,
|
| 52 |
+
stderr=on_stderr, # REQUIRED for Modal!
|
| 53 |
)
|
| 54 |
```
|
| 55 |
|
| 56 |
+
The stderr callback likely helps keep the async event loop properly active in Modal's serverless environment.
|
| 57 |
+
|
| 58 |
+
### 5. ✅ MCP Servers Disabled
|
| 59 |
+
Exa MCP server causes issues on Modal. Set `DISABLE_EXA_MCP=1` to use the built-in WebSearch tool instead.
|
| 60 |
+
|
| 61 |
## Working Configuration
|
| 62 |
|
| 63 |
### modal_agent.py Key Points:
|
| 64 |
- Python 3.11 (matching working example)
|
| 65 |
- Correct sys.path order (mounted code takes priority)
|
| 66 |
- Set `USE_CWD_AS_PROJECT_ROOT=1` before importing agent
|
| 67 |
+
- Set `DISABLE_EXA_MCP=1` to disable MCP servers
|
| 68 |
|
| 69 |
### agent.py Key Points:
|
| 70 |
+
- `ClaudeAgentOptions` with system_prompt, permission_mode, settings, AND stderr callback
|
| 71 |
- Dynamic `PROJECT_ROOT` based on environment variable
|
| 72 |
+
- MCP servers only enabled when not on Modal
|
| 73 |
|
| 74 |
## Test Command
|
| 75 |
|
|
|
|
| 77 |
uv run modal run agents/modal_agent.py --conference-name neurips
|
| 78 |
```
|
| 79 |
|
| 80 |
+
Expected: Multiple messages (28+), web searches, file edits, git operations.
|
| 81 |
|
| 82 |
## Modal Secrets Required
|
| 83 |
|
agents/agent.py
CHANGED
|
@@ -25,6 +25,7 @@ from claude_agent_sdk import (
|
|
| 25 |
UserMessage,
|
| 26 |
query,
|
| 27 |
)
|
|
|
|
| 28 |
|
| 29 |
# Script directory for resolving relative paths
|
| 30 |
SCRIPT_DIR = Path(__file__).parent
|
|
@@ -163,10 +164,18 @@ async def find_conference_deadlines(conference_name: str) -> None:
|
|
| 163 |
|
| 164 |
# Only pass mcp_servers if we have any configured
|
| 165 |
# Passing empty dict or MCP servers can cause issues in some environments
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
options_kwargs = {
|
| 167 |
"system_prompt": system_prompt,
|
| 168 |
"permission_mode": "bypassPermissions",
|
| 169 |
"settings": settings_path,
|
|
|
|
| 170 |
}
|
| 171 |
if mcp_servers:
|
| 172 |
options_kwargs["mcp_servers"] = mcp_servers
|
|
@@ -185,8 +194,6 @@ async def find_conference_deadlines(conference_name: str) -> None:
|
|
| 185 |
|
| 186 |
print(f"Starting agent query with settings: {settings_path}")
|
| 187 |
print(f"Settings path exists: {Path(settings_path).exists()}")
|
| 188 |
-
print(f"System prompt length: {len(system_prompt)}")
|
| 189 |
-
print(f"Conference data loaded: {len(conference_data)} characters")
|
| 190 |
|
| 191 |
message_count = 0
|
| 192 |
try:
|
|
|
|
| 25 |
UserMessage,
|
| 26 |
query,
|
| 27 |
)
|
| 28 |
+
from claude_agent_sdk.types import McpHttpServerConfig
|
| 29 |
|
| 30 |
# Script directory for resolving relative paths
|
| 31 |
SCRIPT_DIR = Path(__file__).parent
|
|
|
|
| 164 |
|
| 165 |
# Only pass mcp_servers if we have any configured
|
| 166 |
# Passing empty dict or MCP servers can cause issues in some environments
|
| 167 |
+
|
| 168 |
+
# IMPORTANT: stderr callback is required for Modal to work correctly!
|
| 169 |
+
# Without it, the SDK only returns SystemMessage (init) and then exits.
|
| 170 |
+
# See agents/MODAL_DEBUGGING.md for details.
|
| 171 |
+
def on_stderr(data: str):
|
| 172 |
+
print(f"[stderr] {data.strip()}")
|
| 173 |
+
|
| 174 |
options_kwargs = {
|
| 175 |
"system_prompt": system_prompt,
|
| 176 |
"permission_mode": "bypassPermissions",
|
| 177 |
"settings": settings_path,
|
| 178 |
+
"stderr": on_stderr, # Capture stderr to see what Claude Code is doing
|
| 179 |
}
|
| 180 |
if mcp_servers:
|
| 181 |
options_kwargs["mcp_servers"] = mcp_servers
|
|
|
|
| 194 |
|
| 195 |
print(f"Starting agent query with settings: {settings_path}")
|
| 196 |
print(f"Settings path exists: {Path(settings_path).exists()}")
|
|
|
|
|
|
|
| 197 |
|
| 198 |
message_count = 0
|
| 199 |
try:
|