sandboxy 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. sandboxy/__init__.py +3 -0
  2. sandboxy/agents/__init__.py +21 -0
  3. sandboxy/agents/base.py +66 -0
  4. sandboxy/agents/llm_prompt.py +308 -0
  5. sandboxy/agents/loader.py +222 -0
  6. sandboxy/api/__init__.py +5 -0
  7. sandboxy/api/app.py +76 -0
  8. sandboxy/api/routes/__init__.py +1 -0
  9. sandboxy/api/routes/agents.py +92 -0
  10. sandboxy/api/routes/local.py +1388 -0
  11. sandboxy/api/routes/tools.py +106 -0
  12. sandboxy/cli/__init__.py +1 -0
  13. sandboxy/cli/main.py +1196 -0
  14. sandboxy/cli/type_detector.py +48 -0
  15. sandboxy/config.py +49 -0
  16. sandboxy/core/__init__.py +1 -0
  17. sandboxy/core/async_runner.py +824 -0
  18. sandboxy/core/mdl_parser.py +441 -0
  19. sandboxy/core/runner.py +599 -0
  20. sandboxy/core/safe_eval.py +165 -0
  21. sandboxy/core/state.py +234 -0
  22. sandboxy/datasets/__init__.py +20 -0
  23. sandboxy/datasets/loader.py +193 -0
  24. sandboxy/datasets/runner.py +442 -0
  25. sandboxy/errors.py +166 -0
  26. sandboxy/local/context.py +235 -0
  27. sandboxy/local/results.py +173 -0
  28. sandboxy/logging.py +31 -0
  29. sandboxy/mcp/__init__.py +25 -0
  30. sandboxy/mcp/client.py +360 -0
  31. sandboxy/mcp/wrapper.py +99 -0
  32. sandboxy/providers/__init__.py +34 -0
  33. sandboxy/providers/anthropic_provider.py +271 -0
  34. sandboxy/providers/base.py +123 -0
  35. sandboxy/providers/http_client.py +101 -0
  36. sandboxy/providers/openai_provider.py +282 -0
  37. sandboxy/providers/openrouter.py +958 -0
  38. sandboxy/providers/registry.py +199 -0
  39. sandboxy/scenarios/__init__.py +11 -0
  40. sandboxy/scenarios/comparison.py +491 -0
  41. sandboxy/scenarios/loader.py +262 -0
  42. sandboxy/scenarios/runner.py +468 -0
  43. sandboxy/scenarios/unified.py +1434 -0
  44. sandboxy/session/__init__.py +21 -0
  45. sandboxy/session/manager.py +278 -0
  46. sandboxy/tools/__init__.py +34 -0
  47. sandboxy/tools/base.py +127 -0
  48. sandboxy/tools/loader.py +270 -0
  49. sandboxy/tools/yaml_tools.py +708 -0
  50. sandboxy/ui/__init__.py +27 -0
  51. sandboxy/ui/dist/assets/index-CgAkYWrJ.css +1 -0
  52. sandboxy/ui/dist/assets/index-D4zoGFcr.js +347 -0
  53. sandboxy/ui/dist/index.html +14 -0
  54. sandboxy/utils/__init__.py +3 -0
  55. sandboxy/utils/time.py +20 -0
  56. sandboxy-0.0.1.dist-info/METADATA +241 -0
  57. sandboxy-0.0.1.dist-info/RECORD +60 -0
  58. sandboxy-0.0.1.dist-info/WHEEL +4 -0
  59. sandboxy-0.0.1.dist-info/entry_points.txt +3 -0
  60. sandboxy-0.0.1.dist-info/licenses/LICENSE +201 -0
sandboxy/api/app.py ADDED
@@ -0,0 +1,76 @@
1
+ """FastAPI application factory for local development."""
2
+
3
+ import logging
4
+ from contextlib import asynccontextmanager
5
+ from pathlib import Path
6
+
7
+ from dotenv import load_dotenv
8
+
9
+ from sandboxy.logging import setup_logging
10
+
11
+ load_dotenv()
12
+ setup_logging()
13
+
14
+ from fastapi import FastAPI
15
+ from fastapi.middleware.cors import CORSMiddleware
16
+ from fastapi.staticfiles import StaticFiles
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @asynccontextmanager
22
+ async def _local_lifespan(app: FastAPI):
23
+ """Lifespan handler for local mode (no database)."""
24
+ yield
25
+
26
+
27
+ def create_local_app(
28
+ root_dir: Path,
29
+ local_ui_path: Path | None = None,
30
+ ) -> FastAPI:
31
+ """Create FastAPI app for local development mode.
32
+
33
+ This version is lightweight and reads from the local filesystem.
34
+
35
+ Args:
36
+ root_dir: Working directory for scenarios/tools/agents.
37
+ local_ui_path: Path to local UI static files.
38
+ """
39
+ from sandboxy.local.context import LocalContext, set_local_context
40
+
41
+ ctx = LocalContext(root_dir=root_dir)
42
+ set_local_context(ctx)
43
+
44
+ app = FastAPI(
45
+ title="Sandboxy Local",
46
+ description="Local development server for Sandboxy",
47
+ version="0.2.0",
48
+ lifespan=_local_lifespan,
49
+ )
50
+
51
+ # CORS - allow all origins in local mode
52
+ app.add_middleware(
53
+ CORSMiddleware,
54
+ allow_origins=["*"],
55
+ allow_credentials=True,
56
+ allow_methods=["*"],
57
+ allow_headers=["*"],
58
+ )
59
+
60
+ # Local routes only
61
+ from sandboxy.api.routes import agents, tools
62
+ from sandboxy.api.routes import local as local_routes
63
+
64
+ app.include_router(local_routes.router, prefix="/api/v1", tags=["local"])
65
+ app.include_router(agents.router, prefix="/api/v1", tags=["agents"])
66
+ app.include_router(tools.router, prefix="/api/v1", tags=["tools"])
67
+
68
+ @app.get("/health")
69
+ async def health_check():
70
+ return {"status": "ok", "mode": "local"}
71
+
72
+ # Serve local UI if available
73
+ if local_ui_path and local_ui_path.exists():
74
+ app.mount("/", StaticFiles(directory=str(local_ui_path), html=True), name="local-ui")
75
+
76
+ return app
@@ -0,0 +1 @@
1
+ """API routes for Sandboxy."""
@@ -0,0 +1,92 @@
1
+ """Agent listing routes."""
2
+
3
+ import logging
4
+
5
+ import yaml
6
+ from fastapi import APIRouter, HTTPException
7
+ from pydantic import BaseModel
8
+
9
+ from sandboxy.local.context import get_local_context
10
+
11
+ logger = logging.getLogger(__name__)
12
+ router = APIRouter()
13
+
14
+
15
+ class AgentResponse(BaseModel):
16
+ """Response model for an agent."""
17
+
18
+ id: str
19
+ name: str
20
+ model: str
21
+ description: str | None = None
22
+ provider: str | None = None
23
+
24
+
25
+ class AgentListResponse(BaseModel):
26
+ """Response model for agent listing."""
27
+
28
+ agents: list[AgentResponse]
29
+ count: int
30
+
31
+
32
+ def _load_agents() -> list[AgentResponse]:
33
+ """Load agents from YAML files in the local agents directory."""
34
+ ctx = get_local_context()
35
+ if not ctx:
36
+ return []
37
+
38
+ agents = []
39
+ agent_dir = ctx.agents_dir
40
+
41
+ if not agent_dir.exists():
42
+ return agents
43
+
44
+ for path in agent_dir.glob("*.y*ml"):
45
+ try:
46
+ content = path.read_text()
47
+ data = yaml.safe_load(content)
48
+ if data and isinstance(data, dict):
49
+ agent_id = data.get("id", path.stem)
50
+ model = data.get("model", "unknown")
51
+
52
+ # Determine provider from model name
53
+ provider = None
54
+ if "gpt" in model.lower():
55
+ provider = "openai"
56
+ elif "claude" in model.lower():
57
+ provider = "anthropic"
58
+ elif "llama" in model.lower() or "mistral" in model.lower():
59
+ provider = "local"
60
+
61
+ agents.append(
62
+ AgentResponse(
63
+ id=agent_id,
64
+ name=data.get("name", agent_id),
65
+ model=model,
66
+ description=data.get("description"),
67
+ provider=provider,
68
+ )
69
+ )
70
+ except Exception as e:
71
+ logger.warning(f"Failed to load agent from {path}: {e}")
72
+ continue
73
+
74
+ return agents
75
+
76
+
77
+ @router.get("/agents", response_model=AgentListResponse)
78
+ async def list_agents():
79
+ """List all available agents."""
80
+ agents = _load_agents()
81
+ return AgentListResponse(agents=agents, count=len(agents))
82
+
83
+
84
+ @router.get("/agents/{agent_id}", response_model=AgentResponse)
85
+ async def get_agent(agent_id: str):
86
+ """Get an agent by ID."""
87
+ agents = _load_agents()
88
+ for agent in agents:
89
+ if agent.id == agent_id:
90
+ return agent
91
+
92
+ raise HTTPException(status_code=404, detail="Agent not found")