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.
- sandboxy/__init__.py +3 -0
- sandboxy/agents/__init__.py +21 -0
- sandboxy/agents/base.py +66 -0
- sandboxy/agents/llm_prompt.py +308 -0
- sandboxy/agents/loader.py +222 -0
- sandboxy/api/__init__.py +5 -0
- sandboxy/api/app.py +76 -0
- sandboxy/api/routes/__init__.py +1 -0
- sandboxy/api/routes/agents.py +92 -0
- sandboxy/api/routes/local.py +1388 -0
- sandboxy/api/routes/tools.py +106 -0
- sandboxy/cli/__init__.py +1 -0
- sandboxy/cli/main.py +1196 -0
- sandboxy/cli/type_detector.py +48 -0
- sandboxy/config.py +49 -0
- sandboxy/core/__init__.py +1 -0
- sandboxy/core/async_runner.py +824 -0
- sandboxy/core/mdl_parser.py +441 -0
- sandboxy/core/runner.py +599 -0
- sandboxy/core/safe_eval.py +165 -0
- sandboxy/core/state.py +234 -0
- sandboxy/datasets/__init__.py +20 -0
- sandboxy/datasets/loader.py +193 -0
- sandboxy/datasets/runner.py +442 -0
- sandboxy/errors.py +166 -0
- sandboxy/local/context.py +235 -0
- sandboxy/local/results.py +173 -0
- sandboxy/logging.py +31 -0
- sandboxy/mcp/__init__.py +25 -0
- sandboxy/mcp/client.py +360 -0
- sandboxy/mcp/wrapper.py +99 -0
- sandboxy/providers/__init__.py +34 -0
- sandboxy/providers/anthropic_provider.py +271 -0
- sandboxy/providers/base.py +123 -0
- sandboxy/providers/http_client.py +101 -0
- sandboxy/providers/openai_provider.py +282 -0
- sandboxy/providers/openrouter.py +958 -0
- sandboxy/providers/registry.py +199 -0
- sandboxy/scenarios/__init__.py +11 -0
- sandboxy/scenarios/comparison.py +491 -0
- sandboxy/scenarios/loader.py +262 -0
- sandboxy/scenarios/runner.py +468 -0
- sandboxy/scenarios/unified.py +1434 -0
- sandboxy/session/__init__.py +21 -0
- sandboxy/session/manager.py +278 -0
- sandboxy/tools/__init__.py +34 -0
- sandboxy/tools/base.py +127 -0
- sandboxy/tools/loader.py +270 -0
- sandboxy/tools/yaml_tools.py +708 -0
- sandboxy/ui/__init__.py +27 -0
- sandboxy/ui/dist/assets/index-CgAkYWrJ.css +1 -0
- sandboxy/ui/dist/assets/index-D4zoGFcr.js +347 -0
- sandboxy/ui/dist/index.html +14 -0
- sandboxy/utils/__init__.py +3 -0
- sandboxy/utils/time.py +20 -0
- sandboxy-0.0.1.dist-info/METADATA +241 -0
- sandboxy-0.0.1.dist-info/RECORD +60 -0
- sandboxy-0.0.1.dist-info/WHEEL +4 -0
- sandboxy-0.0.1.dist-info/entry_points.txt +3 -0
- 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")
|