mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-21 04:43:09 +00:00
feat: increase batch size limits to 15 and add testing_batch_size setting
Batch size configuration:
- Increase coding agent batch size limit from 1-3 to 1-15
- Increase testing agent batch size limit from 1-5 to 1-15
- Add separate `testing_batch_size` setting (previously only CLI-configurable)
- Pass testing_batch_size through full stack: schema → settings router →
agent router → process manager → CLI flag
UI changes:
- Replace 3-button batch size selector with range slider (1-15)
- Add new Slider component (ui/src/components/ui/slider.tsx)
- Add "Features per Testing Agent" slider in settings panel
- Add custom slider CSS styling for webkit and mozilla
Updated across: CLAUDE.md, autonomous_agent_demo.py, parallel_orchestrator.py,
server/{schemas,routers,services}, and UI types/hooks/components.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -65,7 +65,7 @@ python autonomous_agent_demo.py --project-dir my-app --yolo
|
|||||||
# Parallel mode: run multiple agents concurrently (1-5 agents)
|
# Parallel mode: run multiple agents concurrently (1-5 agents)
|
||||||
python autonomous_agent_demo.py --project-dir my-app --parallel --max-concurrency 3
|
python autonomous_agent_demo.py --project-dir my-app --parallel --max-concurrency 3
|
||||||
|
|
||||||
# Batch mode: implement multiple features per agent session (1-3)
|
# Batch mode: implement multiple features per agent session (1-15)
|
||||||
python autonomous_agent_demo.py --project-dir my-app --batch-size 3
|
python autonomous_agent_demo.py --project-dir my-app --batch-size 3
|
||||||
|
|
||||||
# Batch specific features by ID
|
# Batch specific features by ID
|
||||||
@@ -496,9 +496,9 @@ The orchestrator enforces strict bounds on concurrent processes:
|
|||||||
|
|
||||||
### Multi-Feature Batching
|
### Multi-Feature Batching
|
||||||
|
|
||||||
Agents can implement multiple features per session using `--batch-size` (1-3, default: 3):
|
Agents can implement multiple features per session using `--batch-size` (1-15, default: 3):
|
||||||
- `--batch-size N` - Max features per coding agent batch
|
- `--batch-size N` - Max features per coding agent batch
|
||||||
- `--testing-batch-size N` - Features per testing batch (1-5, default: 3)
|
- `--testing-batch-size N` - Features per testing batch (1-15, default: 3)
|
||||||
- `--batch-features 1,2,3` - Specific feature IDs for batch implementation
|
- `--batch-features 1,2,3` - Specific feature IDs for batch implementation
|
||||||
- `--testing-batch-features 1,2,3` - Specific feature IDs for batch regression testing
|
- `--testing-batch-features 1,2,3` - Specific feature IDs for batch regression testing
|
||||||
- `prompts.py` provides `get_batch_feature_prompt()` for multi-feature prompt generation
|
- `prompts.py` provides `get_batch_feature_prompt()` for multi-feature prompt generation
|
||||||
|
|||||||
@@ -176,14 +176,14 @@ Authentication:
|
|||||||
"--testing-batch-size",
|
"--testing-batch-size",
|
||||||
type=int,
|
type=int,
|
||||||
default=3,
|
default=3,
|
||||||
help="Number of features per testing batch (1-5, default: 3)",
|
help="Number of features per testing batch (1-15, default: 3)",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--batch-size",
|
"--batch-size",
|
||||||
type=int,
|
type=int,
|
||||||
default=3,
|
default=3,
|
||||||
help="Max features per coding agent batch (1-3, default: 3)",
|
help="Max features per coding agent batch (1-15, default: 3)",
|
||||||
)
|
)
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ def _dump_database_state(feature_dicts: list[dict], label: str = ""):
|
|||||||
MAX_PARALLEL_AGENTS = 5
|
MAX_PARALLEL_AGENTS = 5
|
||||||
MAX_TOTAL_AGENTS = 10
|
MAX_TOTAL_AGENTS = 10
|
||||||
DEFAULT_CONCURRENCY = 3
|
DEFAULT_CONCURRENCY = 3
|
||||||
DEFAULT_TESTING_BATCH_SIZE = 3 # Number of features per testing batch (1-5)
|
DEFAULT_TESTING_BATCH_SIZE = 3 # Number of features per testing batch (1-15)
|
||||||
POLL_INTERVAL = 5 # seconds between checking for ready features
|
POLL_INTERVAL = 5 # seconds between checking for ready features
|
||||||
MAX_FEATURE_RETRIES = 3 # Maximum times to retry a failed feature
|
MAX_FEATURE_RETRIES = 3 # Maximum times to retry a failed feature
|
||||||
INITIALIZER_TIMEOUT = 1800 # 30 minutes timeout for initializer
|
INITIALIZER_TIMEOUT = 1800 # 30 minutes timeout for initializer
|
||||||
@@ -168,7 +168,7 @@ class ParallelOrchestrator:
|
|||||||
yolo_mode: Whether to run in YOLO mode (skip testing agents entirely)
|
yolo_mode: Whether to run in YOLO mode (skip testing agents entirely)
|
||||||
testing_agent_ratio: Number of regression testing agents to maintain (0-3).
|
testing_agent_ratio: Number of regression testing agents to maintain (0-3).
|
||||||
0 = disabled, 1-3 = maintain that many testing agents running independently.
|
0 = disabled, 1-3 = maintain that many testing agents running independently.
|
||||||
testing_batch_size: Number of features to include per testing session (1-5).
|
testing_batch_size: Number of features to include per testing session (1-15).
|
||||||
Each testing agent receives this many features to regression test.
|
Each testing agent receives this many features to regression test.
|
||||||
on_output: Callback for agent output (feature_id, line)
|
on_output: Callback for agent output (feature_id, line)
|
||||||
on_status: Callback for agent status changes (feature_id, status)
|
on_status: Callback for agent status changes (feature_id, status)
|
||||||
@@ -178,8 +178,8 @@ class ParallelOrchestrator:
|
|||||||
self.model = model
|
self.model = model
|
||||||
self.yolo_mode = yolo_mode
|
self.yolo_mode = yolo_mode
|
||||||
self.testing_agent_ratio = min(max(testing_agent_ratio, 0), 3) # Clamp 0-3
|
self.testing_agent_ratio = min(max(testing_agent_ratio, 0), 3) # Clamp 0-3
|
||||||
self.testing_batch_size = min(max(testing_batch_size, 1), 5) # Clamp 1-5
|
self.testing_batch_size = min(max(testing_batch_size, 1), 15) # Clamp 1-15
|
||||||
self.batch_size = min(max(batch_size, 1), 3) # Clamp 1-3
|
self.batch_size = min(max(batch_size, 1), 15) # Clamp 1-15
|
||||||
self.on_output = on_output
|
self.on_output = on_output
|
||||||
self.on_status = on_status
|
self.on_status = on_status
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ from ..utils.project_helpers import get_project_path as _get_project_path
|
|||||||
from ..utils.validation import validate_project_name
|
from ..utils.validation import validate_project_name
|
||||||
|
|
||||||
|
|
||||||
def _get_settings_defaults() -> tuple[bool, str, int, bool, int]:
|
def _get_settings_defaults() -> tuple[bool, str, int, bool, int, int]:
|
||||||
"""Get defaults from global settings.
|
"""Get defaults from global settings.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (yolo_mode, model, testing_agent_ratio, playwright_headless, batch_size)
|
Tuple of (yolo_mode, model, testing_agent_ratio, playwright_headless, batch_size, testing_batch_size)
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
root = Path(__file__).parent.parent.parent
|
root = Path(__file__).parent.parent.parent
|
||||||
@@ -47,7 +47,12 @@ def _get_settings_defaults() -> tuple[bool, str, int, bool, int]:
|
|||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
batch_size = 3
|
batch_size = 3
|
||||||
|
|
||||||
return yolo_mode, model, testing_agent_ratio, playwright_headless, batch_size
|
try:
|
||||||
|
testing_batch_size = int(settings.get("testing_batch_size", "3"))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
testing_batch_size = 3
|
||||||
|
|
||||||
|
return yolo_mode, model, testing_agent_ratio, playwright_headless, batch_size, testing_batch_size
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/projects/{project_name}/agent", tags=["agent"])
|
router = APIRouter(prefix="/api/projects/{project_name}/agent", tags=["agent"])
|
||||||
@@ -96,7 +101,7 @@ async def start_agent(
|
|||||||
manager = get_project_manager(project_name)
|
manager = get_project_manager(project_name)
|
||||||
|
|
||||||
# Get defaults from global settings if not provided in request
|
# Get defaults from global settings if not provided in request
|
||||||
default_yolo, default_model, default_testing_ratio, playwright_headless, default_batch_size = _get_settings_defaults()
|
default_yolo, default_model, default_testing_ratio, playwright_headless, default_batch_size, default_testing_batch_size = _get_settings_defaults()
|
||||||
|
|
||||||
yolo_mode = request.yolo_mode if request.yolo_mode is not None else default_yolo
|
yolo_mode = request.yolo_mode if request.yolo_mode is not None else default_yolo
|
||||||
model = request.model if request.model else default_model
|
model = request.model if request.model else default_model
|
||||||
@@ -104,6 +109,7 @@ async def start_agent(
|
|||||||
testing_agent_ratio = request.testing_agent_ratio if request.testing_agent_ratio is not None else default_testing_ratio
|
testing_agent_ratio = request.testing_agent_ratio if request.testing_agent_ratio is not None else default_testing_ratio
|
||||||
|
|
||||||
batch_size = default_batch_size
|
batch_size = default_batch_size
|
||||||
|
testing_batch_size = default_testing_batch_size
|
||||||
|
|
||||||
success, message = await manager.start(
|
success, message = await manager.start(
|
||||||
yolo_mode=yolo_mode,
|
yolo_mode=yolo_mode,
|
||||||
@@ -112,6 +118,7 @@ async def start_agent(
|
|||||||
testing_agent_ratio=testing_agent_ratio,
|
testing_agent_ratio=testing_agent_ratio,
|
||||||
playwright_headless=playwright_headless,
|
playwright_headless=playwright_headless,
|
||||||
batch_size=batch_size,
|
batch_size=batch_size,
|
||||||
|
testing_batch_size=testing_batch_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Notify scheduler of manual start (to prevent auto-stop during scheduled window)
|
# Notify scheduler of manual start (to prevent auto-stop during scheduled window)
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ async def get_settings():
|
|||||||
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
|
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
|
||||||
playwright_headless=_parse_bool(all_settings.get("playwright_headless"), default=True),
|
playwright_headless=_parse_bool(all_settings.get("playwright_headless"), default=True),
|
||||||
batch_size=_parse_int(all_settings.get("batch_size"), 3),
|
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_provider=api_provider,
|
||||||
api_base_url=all_settings.get("api_base_url"),
|
api_base_url=all_settings.get("api_base_url"),
|
||||||
api_has_auth_token=bool(all_settings.get("api_auth_token")),
|
api_has_auth_token=bool(all_settings.get("api_auth_token")),
|
||||||
@@ -138,6 +139,9 @@ async def update_settings(update: SettingsUpdate):
|
|||||||
if update.batch_size is not None:
|
if update.batch_size is not None:
|
||||||
set_setting("batch_size", str(update.batch_size))
|
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
|
# API provider settings
|
||||||
if update.api_provider is not None:
|
if update.api_provider is not None:
|
||||||
old_provider = get_setting("api_provider", "claude")
|
old_provider = get_setting("api_provider", "claude")
|
||||||
@@ -177,6 +181,7 @@ async def update_settings(update: SettingsUpdate):
|
|||||||
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
|
testing_agent_ratio=_parse_int(all_settings.get("testing_agent_ratio"), 1),
|
||||||
playwright_headless=_parse_bool(all_settings.get("playwright_headless"), default=True),
|
playwright_headless=_parse_bool(all_settings.get("playwright_headless"), default=True),
|
||||||
batch_size=_parse_int(all_settings.get("batch_size"), 3),
|
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_provider=api_provider,
|
||||||
api_base_url=all_settings.get("api_base_url"),
|
api_base_url=all_settings.get("api_base_url"),
|
||||||
api_has_auth_token=bool(all_settings.get("api_auth_token")),
|
api_has_auth_token=bool(all_settings.get("api_auth_token")),
|
||||||
|
|||||||
@@ -444,7 +444,8 @@ class SettingsResponse(BaseModel):
|
|||||||
ollama_mode: bool = False # True when api_provider is "ollama"
|
ollama_mode: bool = False # True when api_provider is "ollama"
|
||||||
testing_agent_ratio: int = 1 # Regression testing agents (0-3)
|
testing_agent_ratio: int = 1 # Regression testing agents (0-3)
|
||||||
playwright_headless: bool = True
|
playwright_headless: bool = True
|
||||||
batch_size: int = 3 # Features per coding agent batch (1-3)
|
batch_size: int = 3 # Features per coding agent batch (1-15)
|
||||||
|
testing_batch_size: int = 3 # Features per testing agent batch (1-15)
|
||||||
api_provider: str = "claude"
|
api_provider: str = "claude"
|
||||||
api_base_url: str | None = None
|
api_base_url: str | None = None
|
||||||
api_has_auth_token: bool = False # Never expose actual token
|
api_has_auth_token: bool = False # Never expose actual token
|
||||||
@@ -463,7 +464,8 @@ class SettingsUpdate(BaseModel):
|
|||||||
model: str | None = None
|
model: str | None = None
|
||||||
testing_agent_ratio: int | None = None # 0-3
|
testing_agent_ratio: int | None = None # 0-3
|
||||||
playwright_headless: bool | None = None
|
playwright_headless: bool | None = None
|
||||||
batch_size: int | None = None # Features per agent batch (1-3)
|
batch_size: int | None = None # Features per agent batch (1-15)
|
||||||
|
testing_batch_size: int | None = None # Features per testing agent batch (1-15)
|
||||||
api_provider: str | None = None
|
api_provider: str | None = None
|
||||||
api_base_url: str | None = Field(None, max_length=500)
|
api_base_url: str | None = Field(None, max_length=500)
|
||||||
api_auth_token: str | None = Field(None, max_length=500) # Write-only, never returned
|
api_auth_token: str | None = Field(None, max_length=500) # Write-only, never returned
|
||||||
@@ -500,8 +502,15 @@ class SettingsUpdate(BaseModel):
|
|||||||
@field_validator('batch_size')
|
@field_validator('batch_size')
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_batch_size(cls, v: int | None) -> int | None:
|
def validate_batch_size(cls, v: int | None) -> int | None:
|
||||||
if v is not None and (v < 1 or v > 3):
|
if v is not None and (v < 1 or v > 15):
|
||||||
raise ValueError("batch_size must be between 1 and 3")
|
raise ValueError("batch_size must be between 1 and 15")
|
||||||
|
return v
|
||||||
|
|
||||||
|
@field_validator('testing_batch_size')
|
||||||
|
@classmethod
|
||||||
|
def validate_testing_batch_size(cls, v: int | None) -> int | None:
|
||||||
|
if v is not None and (v < 1 or v > 15):
|
||||||
|
raise ValueError("testing_batch_size must be between 1 and 15")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -374,6 +374,7 @@ class AgentProcessManager:
|
|||||||
testing_agent_ratio: int = 1,
|
testing_agent_ratio: int = 1,
|
||||||
playwright_headless: bool = True,
|
playwright_headless: bool = True,
|
||||||
batch_size: int = 3,
|
batch_size: int = 3,
|
||||||
|
testing_batch_size: int = 3,
|
||||||
) -> tuple[bool, str]:
|
) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
Start the agent as a subprocess.
|
Start the agent as a subprocess.
|
||||||
@@ -440,6 +441,9 @@ class AgentProcessManager:
|
|||||||
# Add --batch-size flag for multi-feature batching
|
# Add --batch-size flag for multi-feature batching
|
||||||
cmd.extend(["--batch-size", str(batch_size)])
|
cmd.extend(["--batch-size", str(batch_size)])
|
||||||
|
|
||||||
|
# Add --testing-batch-size flag for testing agent batching
|
||||||
|
cmd.extend(["--testing-batch-size", str(testing_batch_size)])
|
||||||
|
|
||||||
# Apply headless setting to .playwright/cli.config.json so playwright-cli
|
# Apply headless setting to .playwright/cli.config.json so playwright-cli
|
||||||
# picks it up (the only mechanism it supports for headless control)
|
# picks it up (the only mechanism it supports for headless control)
|
||||||
self._apply_playwright_headless(playwright_headless)
|
self._apply_playwright_headless(playwright_headless)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
|
import { Slider } from '@/components/ui/slider'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -63,6 +64,12 @@ export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleTestingBatchSizeChange = (size: number) => {
|
||||||
|
if (!updateSettings.isPending) {
|
||||||
|
updateSettings.mutate({ testing_batch_size: size })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleProviderChange = (providerId: string) => {
|
const handleProviderChange = (providerId: string) => {
|
||||||
if (!updateSettings.isPending) {
|
if (!updateSettings.isPending) {
|
||||||
updateSettings.mutate({ api_provider: providerId })
|
updateSettings.mutate({ api_provider: providerId })
|
||||||
@@ -432,28 +439,34 @@ export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Features per Agent */}
|
{/* Features per Coding Agent */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="font-medium">Features per Agent</Label>
|
<Label className="font-medium">Features per Coding Agent</Label>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Number of features assigned to each coding agent
|
Number of features assigned to each coding agent session
|
||||||
</p>
|
</p>
|
||||||
<div className="flex rounded-lg border overflow-hidden">
|
<Slider
|
||||||
{[1, 2, 3].map((size) => (
|
min={1}
|
||||||
<button
|
max={15}
|
||||||
key={size}
|
value={settings.batch_size ?? 3}
|
||||||
onClick={() => handleBatchSizeChange(size)}
|
onChange={handleBatchSizeChange}
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
className={`flex-1 py-2 px-3 text-sm font-medium transition-colors ${
|
/>
|
||||||
(settings.batch_size ?? 1) === size
|
</div>
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'bg-background text-foreground hover:bg-muted'
|
{/* Features per Testing Agent */}
|
||||||
} ${isSaving ? 'opacity-50 cursor-not-allowed' : ''}`}
|
<div className="space-y-2">
|
||||||
>
|
<Label className="font-medium">Features per Testing Agent</Label>
|
||||||
{size}
|
<p className="text-sm text-muted-foreground">
|
||||||
</button>
|
Number of features assigned to each testing agent session
|
||||||
))}
|
</p>
|
||||||
</div>
|
<Slider
|
||||||
|
min={1}
|
||||||
|
max={15}
|
||||||
|
value={settings.testing_batch_size ?? 3}
|
||||||
|
onChange={handleTestingBatchSizeChange}
|
||||||
|
disabled={isSaving}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Update Error */}
|
{/* Update Error */}
|
||||||
|
|||||||
44
ui/src/components/ui/slider.tsx
Normal file
44
ui/src/components/ui/slider.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
value: number
|
||||||
|
onChange: (value: number) => void
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Slider({
|
||||||
|
className,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
disabled,
|
||||||
|
...props
|
||||||
|
}: SliderProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("flex items-center gap-3", className)}>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(Number(e.target.value))}
|
||||||
|
disabled={disabled}
|
||||||
|
className={cn(
|
||||||
|
"slider-input h-2 w-full cursor-pointer appearance-none rounded-full bg-input transition-colors",
|
||||||
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||||
|
disabled && "cursor-not-allowed opacity-50"
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<span className="min-w-[2ch] text-center text-sm font-semibold tabular-nums">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Slider }
|
||||||
@@ -302,6 +302,7 @@ const DEFAULT_SETTINGS: Settings = {
|
|||||||
testing_agent_ratio: 1,
|
testing_agent_ratio: 1,
|
||||||
playwright_headless: true,
|
playwright_headless: true,
|
||||||
batch_size: 3,
|
batch_size: 3,
|
||||||
|
testing_batch_size: 3,
|
||||||
api_provider: 'claude',
|
api_provider: 'claude',
|
||||||
api_base_url: null,
|
api_base_url: null,
|
||||||
api_has_auth_token: false,
|
api_has_auth_token: false,
|
||||||
|
|||||||
@@ -579,7 +579,8 @@ export interface Settings {
|
|||||||
ollama_mode: boolean
|
ollama_mode: boolean
|
||||||
testing_agent_ratio: number // Regression testing agents (0-3)
|
testing_agent_ratio: number // Regression testing agents (0-3)
|
||||||
playwright_headless: boolean
|
playwright_headless: boolean
|
||||||
batch_size: number // Features per coding agent batch (1-3)
|
batch_size: number // Features per coding agent batch (1-15)
|
||||||
|
testing_batch_size: number // Features per testing agent batch (1-15)
|
||||||
api_provider: string
|
api_provider: string
|
||||||
api_base_url: string | null
|
api_base_url: string | null
|
||||||
api_has_auth_token: boolean
|
api_has_auth_token: boolean
|
||||||
@@ -592,6 +593,7 @@ export interface SettingsUpdate {
|
|||||||
testing_agent_ratio?: number
|
testing_agent_ratio?: number
|
||||||
playwright_headless?: boolean
|
playwright_headless?: boolean
|
||||||
batch_size?: number
|
batch_size?: number
|
||||||
|
testing_batch_size?: number
|
||||||
api_provider?: string
|
api_provider?: string
|
||||||
api_base_url?: string
|
api_base_url?: string
|
||||||
api_auth_token?: string
|
api_auth_token?: string
|
||||||
|
|||||||
@@ -1472,3 +1472,53 @@
|
|||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--muted-foreground);
|
background: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Slider (range input) styling
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
.slider-input::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary);
|
||||||
|
border: 2px solid var(--primary-foreground);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 150ms, box-shadow 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input::-webkit-slider-thumb:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input::-moz-range-thumb {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary);
|
||||||
|
border: 2px solid var(--primary-foreground);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 150ms, box-shadow 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input::-moz-range-thumb:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input::-webkit-slider-runnable-track {
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: var(--input);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-input::-moz-range-track {
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: var(--input);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user