diff --git a/src/auto_k8s_pilot/api.py b/src/auto_k8s_pilot/api.py index a66f6f9..78ed694 100644 --- a/src/auto_k8s_pilot/api.py +++ b/src/auto_k8s_pilot/api.py @@ -17,10 +17,8 @@ version=SETTINGS.APP_VERSION, ) -# Setup OpenTelemetry tracing setup_tracing(app) -# Initialize crew_instance = AutoK8sPilot() flow_runner = FlowRunner(crew_instance, CONFIG_DIR) diff --git a/src/auto_k8s_pilot/config/tasks.yaml b/src/auto_k8s_pilot/config/tasks.yaml index 8913e4d..e3c8bfc 100644 --- a/src/auto_k8s_pilot/config/tasks.yaml +++ b/src/auto_k8s_pilot/config/tasks.yaml @@ -45,7 +45,7 @@ cluster_summary: status, and explicit unknowns. No fabricated or assumed components. agent: infra_architect -# --- ArgoCD / GitOps ---------------------------------------------------- +# Argo CD and GitOps argocd_list_apps: description: > Use argocd_tool with op="list_apps". @@ -64,7 +64,7 @@ argocd_app_status_chat_api: inputs: app: chat-api -# ⚠️ Mutating (disabled unless ALLOW_MUTATING=true) +# Mutating; disabled unless ALLOW_MUTATING=true. argocd_sync_chat_api: description: > If and only if ALLOW_MUTATING=true, call argocd_tool with op="app_sync", app="chat-api". @@ -75,7 +75,7 @@ argocd_sync_chat_api: inputs: app: chat-api -# --- Loki / Observability ------------------------------------------------ +# Loki and observability loki_recent_errors_chat_api: description: > Use loki_query to fetch last 30 minutes for {app="chat-api"} with "|= \"ERROR\"" and limit=200. @@ -100,7 +100,7 @@ loki_http_activity_chat_api: minutes: 30 limit: 200 -# --- K8s resource pressure ---------------------------------------------- +# Kubernetes resource pressure k8s_top_nodes: description: > Call kubectl_tool exactly once with action="top", kind="nodes" to fetch @@ -134,7 +134,7 @@ k8s_events_recent: namespace: all kind: events -# --- Cloudflare -------------------------------------------------------------- +# Cloudflare dns_check_records: description: > Use cloudflare_dns_tool with op="list" to fetch DNS records for the configured zone. @@ -153,7 +153,7 @@ dns_get_record_api: inputs: name: "api" -# ⚠️ Mutating guarded by ALLOW_MUTATING +# Mutating; guarded by ALLOW_MUTATING. dns_upsert_record_api: description: > If ALLOW_MUTATING=true, use cloudflare_dns_tool with op="upsert" to ensure an A/AAAA/CNAME for API endpoint. @@ -163,7 +163,7 @@ dns_upsert_record_api: "UPSERT OK: " or safety ERROR. agent: cloudflare_admin -# --- OpenRouter / LLM Gateway ------------------------------------------------ +# OpenRouter and LLM gateway llm_gateway_health: description: > Use openrouter_health_tool with op="models" to verify gateway and list a short preview. @@ -172,7 +172,7 @@ llm_gateway_health: "Models:\nPreview:\n" or explicit ERROR. agent: llm_gateway_observer -# --- MCP-K8s bridge ---------------------------------------------------------- +# MCP Kubernetes bridge mcp_k8s_env_check: description: > Validate env for mcp-server-kubernetes connectivity and produce a client config snippet. @@ -181,7 +181,7 @@ mcp_k8s_env_check: "OK:
\nSnippet:\n" or explicit ERROR with missing keys. agent: mcp_bridge -# --- Incident issue auto-file (GitHub) ---------------------------------- +# GitHub incident filing incident_create_issue_if_needed: description: > If ALLOW_MUTATING=true and previous steps (Loki/Argo/K8s) indicate problems diff --git a/src/auto_k8s_pilot/flow_runner.py b/src/auto_k8s_pilot/flow_runner.py index 554eef8..7d31753 100644 --- a/src/auto_k8s_pilot/flow_runner.py +++ b/src/auto_k8s_pilot/flow_runner.py @@ -102,12 +102,11 @@ def run_flow(self, flow_name: str, inputs: dict) -> dict: flow = self.load_flow(flow_name) steps_config = flow.get("steps", []) - # Load behavior.yaml for task -> agent mapping behavior = self._load_behavior() task_agent_map = {t["id"]: t["agent"] for t in behavior.get("tasks", [])} results: List[Dict[str, Any]] = [] - context: Dict[str, str] = {} # accumulated context from previous steps + context: Dict[str, str] = {} total_start = time.time() for step in steps_config: @@ -115,7 +114,6 @@ def run_flow(self, flow_name: str, inputs: dict) -> dict: if not task_id: continue - # Check if task exists task_method = self.tasks_map.get(task_id) if not task_method: enrich_context( @@ -155,7 +153,7 @@ def run_flow(self, flow_name: str, inputs: dict) -> dict: if deterministic is not None: output, evidence_scope = deterministic else: - task = task_method() # Get CrewAI Task object + task = task_method() task_context = yaml.safe_dump( {**inputs, **context}, allow_unicode=True, @@ -183,14 +181,12 @@ def run_flow(self, flow_name: str, inputs: dict) -> dict: } ) - # Add to context for next steps context[task_id] = output if evidence_scope: context[f"{task_id}_scope"] = evidence_scope total_duration_ms = int((time.time() - total_start) * 1000) - # Determine final answer final_answer = self._extract_final_answer(results) metadata = self._extract_metadata(results) metadata["total_duration_ms"] = total_duration_ms @@ -373,7 +369,6 @@ def _extract_metadata(self, results: List[Dict[str, Any]]) -> dict: metadata: Dict[str, Any] = {} for r in results: if r["task"] == "cluster_summary": - # Extract first 500 chars as summary metadata["cluster_summary"] = r["output"][:500] if r["output"] else "" if r["task"] == "incident_create_issue_if_needed": if "Created issue" in r.get("output", ""): diff --git a/src/auto_k8s_pilot/main.py b/src/auto_k8s_pilot/main.py index 3f1f27d..d809ff7 100644 --- a/src/auto_k8s_pilot/main.py +++ b/src/auto_k8s_pilot/main.py @@ -7,19 +7,12 @@ warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd") -# --- Local runner for the Crew ------------------------------------------------ -# This file is just an entrypoint to run your crew locally. -# Keep it simple: only pass runtime inputs if you really need to. -# Otherwise, tasks.yaml already describes what to run. - - def run(): """ Run the crew (sequential process by default). """ - # Example: you can override inputs here if needed inputs = { - "namespace": "all", # or "default" + "namespace": "all", "current_year": str(datetime.now().year), } diff --git a/src/auto_k8s_pilot/observability/logger.py b/src/auto_k8s_pilot/observability/logger.py index c8ace82..938273e 100644 --- a/src/auto_k8s_pilot/observability/logger.py +++ b/src/auto_k8s_pilot/observability/logger.py @@ -6,7 +6,6 @@ import structlog from opentelemetry import trace -# Context variables for additional attributes log_context: ContextVar[Dict[str, Any]] = ContextVar("log_context", default={}) @@ -24,7 +23,6 @@ def _add_trace_context(self, event_dict: Dict[str, Any]) -> Dict[str, Any]: event_dict["trace_id"] = format(span_context.trace_id, "032x") event_dict["span_id"] = format(span_context.span_id, "016x") - # Add context variables event_dict.update(log_context.get()) return event_dict @@ -51,7 +49,6 @@ def debug(self, msg: str, **kwargs: Any) -> None: self.logger.debug(msg, **kwargs) -# Initialize structlog structlog.configure( processors=[ structlog.stdlib.filter_by_level, @@ -69,7 +66,6 @@ def debug(self, msg: str, **kwargs: Any) -> None: cache_logger_on_first_use=True, ) -# Base logger base_logger = structlog.get_logger() diff --git a/src/auto_k8s_pilot/observability/tracing.py b/src/auto_k8s_pilot/observability/tracing.py index 3b3ccfa..154f838 100644 --- a/src/auto_k8s_pilot/observability/tracing.py +++ b/src/auto_k8s_pilot/observability/tracing.py @@ -39,13 +39,11 @@ def setup_tracing(app: Any) -> None: logging.getLogger(__name__).info("OpenTelemetry export disabled") return - # Resource with service and Kubernetes metadata resource = Resource( attributes={ "service.name": SETTINGS.OTEL_SERVICE_NAME, "service.version": SETTINGS.APP_VERSION, "deployment.environment": SETTINGS.ENVIRONMENT, - # Kubernetes metadata "k8s.pod.name": SETTINGS.K8S_POD_NAME or "unknown", "k8s.namespace": SETTINGS.K8S_NAMESPACE or "default", "k8s.node.name": SETTINGS.K8S_NODE_NAME or "unknown", @@ -55,14 +53,13 @@ def setup_tracing(app: Any) -> None: } ) - # For HTTP protocol add correct paths + # OTLP/HTTP exporters require a signal-specific path. traces_endpoint = ( f"{otlp_endpoint}/v1/traces" if not otlp_endpoint.endswith("/v1/traces") else otlp_endpoint ) - # === TRACES === provider = TracerProvider(resource=resource) headers = None @@ -83,11 +80,9 @@ def setup_tracing(app: Any) -> None: provider.add_span_processor(trace_processor) trace.set_tracer_provider(provider) - # Configure root logger root_logger = logging.getLogger() root_logger.setLevel(getattr(logging, SETTINGS.LOG_LEVEL.upper())) - # Console handler if SETTINGS.ENVIRONMENT == "production": import json @@ -118,14 +113,12 @@ def format(self, record: logging.LogRecord) -> str: ) root_logger.addHandler(console_handler) - # FastAPI instrumentation FastAPIInstrumentor().instrument_app( app, tracer_provider=provider, server_request_hook=server_request_hook, ) - # Log successful initialization logger = logging.getLogger(__name__) logger.info( "OpenTelemetry initialized", diff --git a/src/auto_k8s_pilot/settings.py b/src/auto_k8s_pilot/settings.py index 7d6410c..546824f 100644 --- a/src/auto_k8s_pilot/settings.py +++ b/src/auto_k8s_pilot/settings.py @@ -32,11 +32,11 @@ class Settings(BaseSettings): # OpenRouter (LLM Gateway) OPENROUTER_BASE_URL: str = "https://openrouter.ai/api/v1" OPENROUTER_API_KEY: Optional[str] = None - OPENROUTER_SITE_URL: Optional[str] = None # optional Referer - OPENROUTER_APP_NAME: Optional[str] = None # optional X-Title + OPENROUTER_SITE_URL: Optional[str] = None + OPENROUTER_APP_NAME: Optional[str] = None # MCP Kubernetes server - MCP_K8S_SERVER_URL: Optional[str] = None # ws:// or wss:// if applicable + MCP_K8S_SERVER_URL: Optional[str] = None MCP_K8S_API_KEY: Optional[str] = None MCP_K8S_INSECURE: bool = False # Optional stdio command, for example "mcp-server-kubernetes". @@ -71,10 +71,8 @@ class Settings(BaseSettings): # Flow DEFAULT_FLOW: str = "k8s-healthcheck" -# Singleton SETTINGS = Settings() -# Paths BASE_DIR = Path(__file__).parent CONFIG_DIR = BASE_DIR / "config" LAYERS_DIR = CONFIG_DIR / "layers"