Spaces:
Sleeping
Sleeping
| """ | |
| config.py | |
| --------- | |
| Central configuration for AutoDevAgent. | |
| All model names, timeouts, retry limits, API keys, and feature flags | |
| live here. Nothing is hardcoded anywhere else in the project — every | |
| other module imports from this file. | |
| Usage: | |
| from config import settings | |
| print(settings.groq_model_primary) | |
| """ | |
| import os | |
| from pydantic_settings import BaseSettings | |
| from pydantic import Field | |
| class Settings(BaseSettings): | |
| """ | |
| Application-wide settings loaded from environment variables. | |
| Pydantic BaseSettings automatically reads values from: | |
| 1. Environment variables (highest priority) | |
| 2. A .env file in the project root (if present) | |
| 3. The default values defined below (fallback) | |
| All API keys must be set as environment variables or in a .env file. | |
| Never hardcode secrets in this file. | |
| """ | |
| # ------------------------------------------------------------------ # | |
| # API Keys # | |
| # ------------------------------------------------------------------ # | |
| groq_api_key: str = Field( | |
| default="", | |
| description="Groq API key. Set via GROQ_API_KEY env var.", | |
| ) | |
| langsmith_api_key: str = Field( | |
| default="", | |
| description="LangSmith API key. Set via LANGSMITH_API_KEY env var.", | |
| ) | |
| langsmith_project: str = Field( | |
| default="autodevagent", | |
| description="LangSmith project name for grouping traces.", | |
| ) | |
| wandb_api_key: str = Field( | |
| default="", | |
| description="Weights & Biases API key. Set via WANDB_API_KEY env var.", | |
| ) | |
| wandb_project: str = Field( | |
| default="autodevagent", | |
| description="W&B project name for grouping benchmark runs.", | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Groq Model Names # | |
| # ------------------------------------------------------------------ # | |
| groq_model_primary: str = Field( | |
| default="meta-llama/llama-4-scout-17b-16e-instruct", | |
| description=( | |
| "Primary Groq model used for planning, code generation, " | |
| "debugging, explanation, and test generation." | |
| ), | |
| ) | |
| groq_model_fast: str = Field( | |
| default="llama-3.1-8b-instant", | |
| description=( | |
| "Fast, cheap model used for quick tasks: " | |
| "auto language detection and error classification. " | |
| "Llama 3.1 8B is sufficient for these classification tasks." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Debug Loop Limits # | |
| # ------------------------------------------------------------------ # | |
| max_debug_retries: int = Field( | |
| default=5, | |
| description=( | |
| "Maximum number of debug iterations per attempt before the " | |
| "pipeline tries a full code regeneration. Prevents infinite loops." | |
| ), | |
| ) | |
| max_regen_attempts: int = Field( | |
| default=2, | |
| description=( | |
| "Number of full code-regeneration attempts allowed when the debug " | |
| "loop exhausts max_debug_retries without passing all tests. " | |
| "Each regeneration calls CodeGeneratorAgent fresh with the full " | |
| "error history as context. After all regen attempts are spent the " | |
| "pipeline escalates to Human-in-the-Loop (for execution failures) " | |
| "or partial-success output (for test failures)." | |
| ), | |
| ) | |
| max_test_fix_retries: int = Field( | |
| default=3, | |
| description=( | |
| "Maximum number of test-fix cycles per regen attempt. " | |
| "A test-fix cycle = tests fail → debug agent rewrites code → " | |
| "execute → test again. This counter is independent of " | |
| "max_debug_retries so execution failures and test failures each " | |
| "have their own retry budget. After all test-fix retries and " | |
| "regen attempts are spent the pipeline outputs the best code " | |
| "produced (partial success) — it never escalates to HITL " | |
| "for test failures because the code already executes correctly." | |
| ), | |
| ) | |
| graph_recursion_limit: int = Field( | |
| default=100, | |
| description=( | |
| "Maximum number of LangGraph node executions allowed in a single " | |
| "pipeline run. LangGraph counts every node firing as one step and " | |
| "aborts with GRAPH_RECURSION_LIMIT if this ceiling is hit. The " | |
| "default of 25 is far too low for AutoDevAgent's worst-case path " | |
| "(upfront nodes + up to 3 debug retries × 2 regens + 3 test-fix " | |
| "cycles × 2 regens = ~60+ node firings). Set to 100 to cover all " | |
| "realistic retry paths while still preventing genuine infinite loops." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Code Execution Timeouts # | |
| # ------------------------------------------------------------------ # | |
| python_execution_timeout: int = Field( | |
| default=15, | |
| description=( | |
| "Maximum seconds allowed for a Python subprocess to run. " | |
| "Kills the process if exceeded and returns a timeout error." | |
| ), | |
| ) | |
| sql_execution_timeout: int = Field( | |
| default=10, | |
| description=( | |
| "Maximum seconds allowed for a SQL query to run against " | |
| "the in-memory SQLite instance." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Flowchart Trigger # | |
| # ------------------------------------------------------------------ # | |
| flowchart_min_lines: int = Field( | |
| default=20, | |
| description=( | |
| "Minimum lines of code required to auto-trigger the " | |
| "Flowchart Agent. Below this threshold, flowcharts are " | |
| "only generated if the user explicitly requests one." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Groq Rate Limit Warning # | |
| # ------------------------------------------------------------------ # | |
| groq_request_timeout: int = Field( | |
| default=30, | |
| description="Timeout in seconds for each individual Groq API call.", | |
| ) | |
| groq_rate_limit_rpm: int = Field( | |
| default=30, | |
| description="Groq free tier rate limit in requests per minute.", | |
| ) | |
| groq_rate_limit_warning_threshold: float = Field( | |
| default=0.8, | |
| description=( | |
| "Fraction of the rate limit at which a warning is shown. " | |
| "At 0.8, a warning fires when 24 of 30 requests are used." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # LangSmith Tracing Toggle # | |
| # ------------------------------------------------------------------ # | |
| enable_langsmith: bool = Field( | |
| default=False, | |
| description=( | |
| "Set to True to enable LangSmith tracing. " | |
| "Requires a valid LANGSMITH_API_KEY with write access in .env." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # W&B Tracking Toggle # | |
| # ------------------------------------------------------------------ # | |
| enable_wandb: bool = Field( | |
| default=False, | |
| description=( | |
| "Set to True to enable W&B experiment tracking. " | |
| "Requires a valid WANDB_API_KEY in .env." | |
| ), | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Pydantic Settings Config # | |
| # ------------------------------------------------------------------ # | |
| model_config = { | |
| # Read from a .env file in the project root if it exists | |
| "env_file": ".env", | |
| "env_file_encoding": "utf-8", | |
| # Ignore extra fields in the .env file gracefully | |
| "extra": "ignore", | |
| } | |
| def configure_langsmith(settings: Settings) -> None: | |
| """ | |
| Set LangSmith environment variables so LangChain auto-traces | |
| every LLM call, tool invocation, and agent step. | |
| This must be called once at startup, before any LangChain objects | |
| are instantiated. LangChain reads these env vars at import time. | |
| Validates the API key before enabling tracing — if the key is | |
| missing or invalid, tracing is silently disabled so the pipeline | |
| still runs without noisy 403 errors flooding the logs. | |
| Args: | |
| settings: The loaded Settings instance. | |
| """ | |
| import logging | |
| _log = logging.getLogger(__name__) | |
| def _disable_tracing(reason: str = "") -> None: | |
| os.environ["LANGCHAIN_TRACING_V2"] = "false" | |
| os.environ["LANGSMITH_TRACING"] = "false" | |
| if reason: | |
| _log.warning("LangSmith tracing disabled — %s", reason) | |
| if not (settings.enable_langsmith and settings.langsmith_api_key): | |
| _disable_tracing() | |
| return | |
| # Validate the key can write traces before enabling | |
| try: | |
| from langsmith import Client | |
| client = Client(api_key=settings.langsmith_api_key) | |
| # Use read_project to verify credentials without side effects | |
| client.list_projects(limit=1) | |
| os.environ["LANGCHAIN_TRACING_V2"] = "true" | |
| os.environ["LANGSMITH_TRACING"] = "true" | |
| os.environ["LANGCHAIN_API_KEY"] = settings.langsmith_api_key | |
| os.environ["LANGCHAIN_PROJECT"] = settings.langsmith_project | |
| _log.info("LangSmith tracing enabled for project '%s'", settings.langsmith_project) | |
| except Exception as e: | |
| _disable_tracing( | |
| f"key validation failed — check LANGSMITH_API_KEY in .env. Error: {e}" | |
| ) | |
| # ------------------------------------------------------------------ # | |
| # Module-level singleton # | |
| # # | |
| # Import `settings` directly everywhere instead of instantiating # | |
| # Settings() in each file. This ensures config is loaded once. # | |
| # # | |
| # Usage: # | |
| # from config import settings # | |
| # ------------------------------------------------------------------ # | |
| settings = Settings() | |
| # Activate LangSmith tracing as soon as config is loaded | |
| configure_langsmith(settings) | |