1010sandbox_image = (
1111 modal .Image .debian_slim (python_version = "3.12" )
1212 .apt_install ("curl" , "git" , "unzip" )
13+ .pip_install ("logfire" )
1314 .run_commands (
1415 # Install Bun
1516 "curl -fsSL https://bun.sh/install | bash" ,
2829
2930# Secrets
3031anthropic_secret = modal .Secret .from_name ("anthropic-api-key" )
32+ logfire_secret = modal .Secret .from_name ("logfire-token" )
3133
3234
3335def run_claude_code_in_sandbox (
@@ -50,7 +52,7 @@ def run_claude_code_in_sandbox(
5052
5153 sb = modal .Sandbox .create (
5254 image = sandbox_image ,
53- secrets = [anthropic_secret ],
55+ secrets = [anthropic_secret , logfire_secret ],
5456 timeout = 600 ,
5557 workdir = "/tmp" ,
5658 )
@@ -74,7 +76,7 @@ def run_claude_code_in_sandbox(
7476 return sb , process
7577
7678
77- @app .function (image = sandbox_image , secrets = [anthropic_secret ], timeout = 600 )
79+ @app .function (image = sandbox_image , secrets = [anthropic_secret , logfire_secret ], timeout = 600 )
7880def run_policy_analysis (
7981 question : str , api_base_url : str = "https://v2.api.policyengine.org"
8082) -> dict :
@@ -86,33 +88,50 @@ def run_policy_analysis(
8688 import os
8789 import subprocess
8890
89- # Write MCP config
90- os .makedirs ("/root/.claude" , exist_ok = True )
91- mcp_config = {
92- "mcpServers" : {"policyengine" : {"type" : "sse" , "url" : f"{ api_base_url } /mcp" }}
93- }
94- with open ("/root/.claude/mcp_servers.json" , "w" ) as f :
95- json .dump (mcp_config , f )
96-
97- # Run Claude Code
98- result = subprocess .run (
99- [
100- "claude" ,
101- "-p" ,
102- question ,
103- "--allowedTools" ,
104- "mcp__policyengine__*,Bash,Read,Grep,Glob,Write,Edit" ,
105- ],
106- capture_output = True ,
107- text = True ,
108- timeout = 540 ,
109- )
91+ import logfire
92+
93+ logfire .configure (service_name = "policyengine-agent-sandbox" )
11094
111- return {
112- "status" : "completed" if result .returncode == 0 else "failed" ,
113- "report" : result .stdout ,
114- "error" : result .stderr if result .returncode != 0 else None ,
115- }
95+ with logfire .span ("run_policy_analysis" , question = question [:100 ], api_base_url = api_base_url ):
96+ # Write MCP config
97+ os .makedirs ("/root/.claude" , exist_ok = True )
98+ mcp_config = {
99+ "mcpServers" : {"policyengine" : {"type" : "sse" , "url" : f"{ api_base_url } /mcp" }}
100+ }
101+ with open ("/root/.claude/mcp_servers.json" , "w" ) as f :
102+ json .dump (mcp_config , f )
103+
104+ logfire .info ("Starting Claude Code" , question = question [:100 ])
105+
106+ # Run Claude Code
107+ result = subprocess .run (
108+ [
109+ "claude" ,
110+ "-p" ,
111+ question ,
112+ "--allowedTools" ,
113+ "mcp__policyengine__*,Bash,Read,Grep,Glob,Write,Edit" ,
114+ ],
115+ capture_output = True ,
116+ text = True ,
117+ timeout = 540 ,
118+ )
119+
120+ logfire .info (
121+ "Claude Code finished" ,
122+ returncode = result .returncode ,
123+ stdout_len = len (result .stdout ),
124+ stderr_len = len (result .stderr ),
125+ )
126+
127+ if result .returncode != 0 :
128+ logfire .error ("Claude Code failed" , stderr = result .stderr [:500 ])
129+
130+ return {
131+ "status" : "completed" if result .returncode == 0 else "failed" ,
132+ "report" : result .stdout ,
133+ "error" : result .stderr if result .returncode != 0 else None ,
134+ }
116135
117136
118137# For local testing
0 commit comments