auto-dev-agent / config.py
Siva sai Yadav
ready for HuggingFace Space deployment
8edee29
Raw
History Blame Contribute Delete
10.8 kB
"""
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)