1818 ProjectDetail ,
1919 ProjectPrompts ,
2020 ProjectPromptsUpdate ,
21+ ProjectSettingsUpdate ,
2122 ProjectStats ,
2223 ProjectSummary ,
2324)
@@ -63,13 +64,23 @@ def _get_registry_functions():
6364 sys .path .insert (0 , str (root ))
6465
6566 from registry import (
67+ get_project_concurrency ,
6668 get_project_path ,
6769 list_registered_projects ,
6870 register_project ,
71+ set_project_concurrency ,
6972 unregister_project ,
7073 validate_project_path ,
7174 )
72- return register_project , unregister_project , get_project_path , list_registered_projects , validate_project_path
75+ return (
76+ register_project ,
77+ unregister_project ,
78+ get_project_path ,
79+ list_registered_projects ,
80+ validate_project_path ,
81+ get_project_concurrency ,
82+ set_project_concurrency ,
83+ )
7384
7485
7586router = APIRouter (prefix = "/api/projects" , tags = ["projects" ])
@@ -102,7 +113,8 @@ def get_project_stats(project_dir: Path) -> ProjectStats:
102113async def list_projects ():
103114 """List all registered projects."""
104115 _init_imports ()
105- _ , _ , _ , list_registered_projects , validate_project_path = _get_registry_functions ()
116+ (_ , _ , _ , list_registered_projects , validate_project_path ,
117+ get_project_concurrency , _ ) = _get_registry_functions ()
106118
107119 projects = list_registered_projects ()
108120 result = []
@@ -123,6 +135,7 @@ async def list_projects():
123135 path = info ["path" ],
124136 has_spec = has_spec ,
125137 stats = stats ,
138+ default_concurrency = info .get ("default_concurrency" , 3 ),
126139 ))
127140
128141 return result
@@ -132,7 +145,8 @@ async def list_projects():
132145async def create_project (project : ProjectCreate ):
133146 """Create a new project at the specified path."""
134147 _init_imports ()
135- register_project , _ , get_project_path , list_registered_projects , _ = _get_registry_functions ()
148+ (register_project , _ , get_project_path , list_registered_projects ,
149+ _ , _ , _ ) = _get_registry_functions ()
136150
137151 name = validate_project_name (project .name )
138152 project_path = Path (project .path ).resolve ()
@@ -203,14 +217,15 @@ async def create_project(project: ProjectCreate):
203217 path = project_path .as_posix (),
204218 has_spec = False , # Just created, no spec yet
205219 stats = ProjectStats (passing = 0 , total = 0 , percentage = 0.0 ),
220+ default_concurrency = 3 ,
206221 )
207222
208223
209224@router .get ("/{name}" , response_model = ProjectDetail )
210225async def get_project (name : str ):
211226 """Get detailed information about a project."""
212227 _init_imports ()
213- _ , _ , get_project_path , _ , _ = _get_registry_functions ()
228+ ( _ , _ , get_project_path , _ , _ , get_project_concurrency , _ ) = _get_registry_functions ()
214229
215230 name = validate_project_name (name )
216231 project_dir = get_project_path (name )
@@ -231,6 +246,7 @@ async def get_project(name: str):
231246 has_spec = has_spec ,
232247 stats = stats ,
233248 prompts_dir = str (prompts_dir ),
249+ default_concurrency = get_project_concurrency (name ),
234250 )
235251
236252
@@ -244,7 +260,7 @@ async def delete_project(name: str, delete_files: bool = False):
244260 delete_files: If True, also delete the project directory and files
245261 """
246262 _init_imports ()
247- _ , unregister_project , get_project_path , _ , _ = _get_registry_functions ()
263+ ( _ , unregister_project , get_project_path , _ , _ , _ , _ ) = _get_registry_functions ()
248264
249265 name = validate_project_name (name )
250266 project_dir = get_project_path (name )
@@ -280,7 +296,7 @@ async def delete_project(name: str, delete_files: bool = False):
280296async def get_project_prompts (name : str ):
281297 """Get the content of project prompt files."""
282298 _init_imports ()
283- _ , _ , get_project_path , _ , _ = _get_registry_functions ()
299+ ( _ , _ , get_project_path , _ , _ , _ , _ ) = _get_registry_functions ()
284300
285301 name = validate_project_name (name )
286302 project_dir = get_project_path (name )
@@ -313,7 +329,7 @@ def read_file(filename: str) -> str:
313329async def update_project_prompts (name : str , prompts : ProjectPromptsUpdate ):
314330 """Update project prompt files."""
315331 _init_imports ()
316- _ , _ , get_project_path , _ , _ = _get_registry_functions ()
332+ ( _ , _ , get_project_path , _ , _ , _ , _ ) = _get_registry_functions ()
317333
318334 name = validate_project_name (name )
319335 project_dir = get_project_path (name )
@@ -343,7 +359,7 @@ def write_file(filename: str, content: str | None):
343359async def get_project_stats_endpoint (name : str ):
344360 """Get current progress statistics for a project."""
345361 _init_imports ()
346- _ , _ , get_project_path , _ , _ = _get_registry_functions ()
362+ ( _ , _ , get_project_path , _ , _ , _ , _ ) = _get_registry_functions ()
347363
348364 name = validate_project_name (name )
349365 project_dir = get_project_path (name )
@@ -355,3 +371,40 @@ async def get_project_stats_endpoint(name: str):
355371 raise HTTPException (status_code = 404 , detail = "Project directory not found" )
356372
357373 return get_project_stats (project_dir )
374+
375+
376+ @router .patch ("/{name}/settings" , response_model = ProjectDetail )
377+ async def update_project_settings (name : str , settings : ProjectSettingsUpdate ):
378+ """Update project-level settings (concurrency, etc.)."""
379+ _init_imports ()
380+ (_ , _ , get_project_path , _ , _ , get_project_concurrency ,
381+ set_project_concurrency ) = _get_registry_functions ()
382+
383+ name = validate_project_name (name )
384+ project_dir = get_project_path (name )
385+
386+ if not project_dir :
387+ raise HTTPException (status_code = 404 , detail = f"Project '{ name } ' not found" )
388+
389+ if not project_dir .exists ():
390+ raise HTTPException (status_code = 404 , detail = "Project directory not found" )
391+
392+ # Update concurrency if provided
393+ if settings .default_concurrency is not None :
394+ success = set_project_concurrency (name , settings .default_concurrency )
395+ if not success :
396+ raise HTTPException (status_code = 500 , detail = "Failed to update concurrency" )
397+
398+ # Return updated project details
399+ has_spec = _check_spec_exists (project_dir )
400+ stats = get_project_stats (project_dir )
401+ prompts_dir = _get_project_prompts_dir (project_dir )
402+
403+ return ProjectDetail (
404+ name = name ,
405+ path = project_dir .as_posix (),
406+ has_spec = has_spec ,
407+ stats = stats ,
408+ prompts_dir = str (prompts_dir ),
409+ default_concurrency = get_project_concurrency (name ),
410+ )
0 commit comments