-
Notifications
You must be signed in to change notification settings - Fork 415
Expand file tree
/
Copy pathsettings.py
More file actions
189 lines (149 loc) · 6.65 KB
/
settings.py
File metadata and controls
189 lines (149 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
"""
Settings Router
===============
API endpoints for global settings management.
Settings are stored in the registry database and shared across all projects.
"""
import mimetypes
import sys
from fastapi import APIRouter
from ..schemas import ModelInfo, ModelsResponse, ProviderInfo, ProvidersResponse, SettingsResponse, SettingsUpdate
from ..services.chat_constants import ROOT_DIR
# Mimetype fix for Windows - must run before StaticFiles is mounted
mimetypes.add_type("text/javascript", ".js", True)
# Ensure root is on sys.path for registry import
if str(ROOT_DIR) not in sys.path:
sys.path.insert(0, str(ROOT_DIR))
from registry import (
API_PROVIDERS,
AVAILABLE_MODELS,
DEFAULT_MODEL,
get_all_settings,
get_setting,
set_setting,
)
router = APIRouter(prefix="/api/settings", tags=["settings"])
def _parse_yolo_mode(value: str | None) -> bool:
"""Parse YOLO mode string to boolean."""
return (value or "false").lower() == "true"
@router.get("/providers", response_model=ProvidersResponse)
async def get_available_providers():
"""Get list of available API providers."""
current = get_setting("api_provider", "claude") or "claude"
providers = []
for pid, pdata in API_PROVIDERS.items():
providers.append(ProviderInfo(
id=pid,
name=pdata["name"],
base_url=pdata.get("base_url"),
models=[ModelInfo(id=m["id"], name=m["name"]) for m in pdata.get("models", [])],
default_model=pdata.get("default_model", ""),
requires_auth=pdata.get("requires_auth", False),
))
return ProvidersResponse(providers=providers, current=current)
@router.get("/models", response_model=ModelsResponse)
async def get_available_models():
"""Get list of available models.
Returns models for the currently selected API provider.
"""
current_provider = get_setting("api_provider", "claude") or "claude"
provider = API_PROVIDERS.get(current_provider)
if provider and current_provider != "claude":
provider_models = provider.get("models", [])
return ModelsResponse(
models=[ModelInfo(id=m["id"], name=m["name"]) for m in provider_models],
default=provider.get("default_model", ""),
)
# Default: return Claude models
return ModelsResponse(
models=[ModelInfo(id=m["id"], name=m["name"]) for m in AVAILABLE_MODELS],
default=DEFAULT_MODEL,
)
def _parse_int(value: str | None, default: int) -> int:
"""Parse integer setting with default fallback."""
if value is None:
return default
try:
return int(value)
except (ValueError, TypeError):
return default
def _parse_bool(value: str | None, default: bool = False) -> bool:
"""Parse boolean setting with default fallback."""
if value is None:
return default
return value.lower() == "true"
@router.get("", response_model=SettingsResponse)
async def get_settings():
"""Get current global settings."""
all_settings = get_all_settings()
api_provider = all_settings.get("api_provider", "claude")
glm_mode = api_provider == "glm"
ollama_mode = api_provider == "ollama"
return SettingsResponse(
yolo_mode=_parse_yolo_mode(all_settings.get("yolo_mode")),
model=all_settings.get("model", DEFAULT_MODEL),
glm_mode=glm_mode,
ollama_mode=ollama_mode,
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
playwright_headless=True, # Always headless - embedded browser view replaces desktop windows
batch_size=_parse_int(all_settings.get("batch_size"), 3),
testing_batch_size=_parse_int(all_settings.get("testing_batch_size"), 3),
api_provider=api_provider,
api_base_url=all_settings.get("api_base_url"),
api_has_auth_token=bool(all_settings.get("api_auth_token")),
api_model=all_settings.get("api_model"),
)
@router.patch("", response_model=SettingsResponse)
async def update_settings(update: SettingsUpdate):
"""Update global settings."""
if update.yolo_mode is not None:
set_setting("yolo_mode", "true" if update.yolo_mode else "false")
if update.model is not None:
set_setting("model", update.model)
if update.testing_agent_ratio is not None:
set_setting("testing_agent_ratio", str(update.testing_agent_ratio))
# playwright_headless is no longer user-configurable; always headless
# with embedded browser view panel in the UI
if update.batch_size is not None:
set_setting("batch_size", str(update.batch_size))
if update.testing_batch_size is not None:
set_setting("testing_batch_size", str(update.testing_batch_size))
# API provider settings
if update.api_provider is not None:
old_provider = get_setting("api_provider", "claude")
set_setting("api_provider", update.api_provider)
# When provider changes, auto-set defaults for the new provider
if update.api_provider != old_provider:
provider = API_PROVIDERS.get(update.api_provider)
if provider:
# Auto-set base URL from provider definition
if provider.get("base_url"):
set_setting("api_base_url", provider["base_url"])
# Auto-set model to provider's default
if provider.get("default_model") and update.api_model is None:
set_setting("api_model", provider["default_model"])
if update.api_base_url is not None:
set_setting("api_base_url", update.api_base_url)
if update.api_auth_token is not None:
set_setting("api_auth_token", update.api_auth_token)
if update.api_model is not None:
set_setting("api_model", update.api_model)
# Return updated settings
all_settings = get_all_settings()
api_provider = all_settings.get("api_provider", "claude")
glm_mode = api_provider == "glm"
ollama_mode = api_provider == "ollama"
return SettingsResponse(
yolo_mode=_parse_yolo_mode(all_settings.get("yolo_mode")),
model=all_settings.get("model", DEFAULT_MODEL),
glm_mode=glm_mode,
ollama_mode=ollama_mode,
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
playwright_headless=True, # Always headless - embedded browser view replaces desktop windows
batch_size=_parse_int(all_settings.get("batch_size"), 3),
testing_batch_size=_parse_int(all_settings.get("testing_batch_size"), 3),
api_provider=api_provider,
api_base_url=all_settings.get("api_base_url"),
api_has_auth_token=bool(all_settings.get("api_auth_token")),
api_model=all_settings.get("api_model"),
)