From 19e63ae6dfa47bf3e1d72777f6b9f83302667d7c Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 22 Mar 2026 23:01:24 -0700 Subject: [PATCH] =?UTF-8?q?feat(api):=20fix:=20DPLAN-0049=20waves=201-2=20?= =?UTF-8?q?=E2=80=94=20help=5Ftext,=20silent=5Fcatch,=20commented=5Flogger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: @api --- src/aipass/api/apps/api.py | 2 +- src/aipass/api/apps/handlers/auth/env.py | 2 + src/aipass/api/apps/handlers/auth/keys.py | 5 ++ .../api/apps/handlers/config/provider.py | 10 ++-- src/aipass/api/apps/handlers/google/auth.py | 15 ++++-- .../apps/handlers/google/service_factory.py | 11 +++-- .../api/apps/handlers/json/json_handler.py | 16 +++++-- .../api/apps/handlers/openrouter/caller.py | 46 +++++++++--------- .../api/apps/handlers/openrouter/client.py | 33 ++++++------- .../api/apps/handlers/openrouter/models.py | 45 +++++++++--------- .../api/apps/handlers/openrouter/provision.py | 20 ++++---- .../api/apps/handlers/usage/aggregation.py | 47 ++++++++++--------- src/aipass/api/apps/handlers/usage/cleanup.py | 21 +++++---- .../api/apps/handlers/usage/tracking.py | 21 +++++---- src/aipass/api/apps/modules/api_key.py | 2 +- src/aipass/api/apps/modules/google_client.py | 4 +- .../api/apps/modules/openrouter_client.py | 3 +- src/aipass/api/apps/modules/usage_tracker.py | 4 +- 18 files changed, 169 insertions(+), 138 deletions(-) diff --git a/src/aipass/api/apps/api.py b/src/aipass/api/apps/api.py index 45c7510c..849d65d6 100644 --- a/src/aipass/api/apps/api.py +++ b/src/aipass/api/apps/api.py @@ -272,7 +272,7 @@ def main(): return 0 else: logger.warning(f"Unknown command: {command}") - error(f"Unknown command: {command}", suggestion="Run 'python3 api.py --help' for available commands") + error(f"Unknown command: {command}", suggestion="Run 'drone @api --help' for available commands") return 1 if __name__ == "__main__": diff --git a/src/aipass/api/apps/handlers/auth/env.py b/src/aipass/api/apps/handlers/auth/env.py index d64d2d74..a3cc2405 100644 --- a/src/aipass/api/apps/handlers/auth/env.py +++ b/src/aipass/api/apps/handlers/auth/env.py @@ -93,6 +93,7 @@ def read_env_file(env_var: str, search_paths: Optional[List[Path]] = None) -> Op return value.strip() except Exception as e: # Error reading env_file + logger.warning(f"Error reading env file '{env_file}': {e}") continue # Variable not found in any .env file @@ -136,6 +137,7 @@ def read_env_file_dict(env_path: Path) -> Dict[str, str]: except Exception as e: # Error reading env_path + logger.error(f"Error reading env file '{env_path}': {e}") return env_dict diff --git a/src/aipass/api/apps/handlers/auth/keys.py b/src/aipass/api/apps/handlers/auth/keys.py index 2bfab7e3..cf160f1f 100644 --- a/src/aipass/api/apps/handlers/auth/keys.py +++ b/src/aipass/api/apps/handlers/auth/keys.py @@ -28,6 +28,9 @@ import sys import os from typing import Optional, Dict, Any +# Logging +from aipass.prax import logger + # Internal handlers from aipass.api.apps.handlers.auth.env import read_env_file @@ -121,6 +124,7 @@ def get_api_key(provider: str = "openrouter") -> Optional[str]: except Exception as e: # Failed to get key + logger.error(f"Failed to get API key for provider '{provider}': {e}") return None @@ -163,6 +167,7 @@ def get_key_from_config(provider: str) -> Optional[str]: except Exception as e: # Error reading config + logger.error(f"Error reading config for provider '{provider}': {e}") return None diff --git a/src/aipass/api/apps/handlers/config/provider.py b/src/aipass/api/apps/handlers/config/provider.py index 6788def4..613c57ee 100644 --- a/src/aipass/api/apps/handlers/config/provider.py +++ b/src/aipass/api/apps/handlers/config/provider.py @@ -128,10 +128,10 @@ def load_provider_config(provider: str = "openrouter") -> Optional[Dict[str, Any return None except json.JSONDecodeError as e: - # Invalid JSON in config file + logger.error(f"Invalid JSON in provider config: {e}") return None except Exception as e: - # Failed to load provider config + logger.error(f"Failed to load provider config: {e}") return None @@ -153,7 +153,7 @@ def get_full_config() -> Optional[Dict[str, Any]]: return json.load(f) except Exception as e: - # Failed to load full config + logger.error(f"Failed to load full config: {e}") return None @@ -258,7 +258,7 @@ def update_full_config(updates: Dict[str, Any]) -> bool: return True except Exception as e: - # Failed to update full config + logger.error(f"Failed to update full config: {e}") return False @@ -340,7 +340,7 @@ def _create_default_config() -> bool: return True except Exception as e: - # Failed to create default config + logger.error(f"Failed to create default config: {e}") return False diff --git a/src/aipass/api/apps/handlers/google/auth.py b/src/aipass/api/apps/handlers/google/auth.py index 11035141..4c921af2 100644 --- a/src/aipass/api/apps/handlers/google/auth.py +++ b/src/aipass/api/apps/handlers/google/auth.py @@ -23,6 +23,9 @@ Consumers get authenticated credentials, they decide what to do with them. from pathlib import Path from typing import Optional +# Logging +from aipass.prax import logger + # JSON handler from aipass.api.apps.handlers.json import json_handler @@ -51,7 +54,8 @@ try: from google.auth.transport.requests import Request from google_auth_oauthlib.flow import InstalledAppFlow GOOGLE_AUTH_AVAILABLE = True -except ImportError: +except ImportError as e: + logger.warning(f"Google auth libraries not available: {e}") GOOGLE_AUTH_AVAILABLE = False Credentials = None # type: ignore[assignment, misc] Request = None # type: ignore[assignment, misc] @@ -90,7 +94,8 @@ def load_credentials(scopes: Optional[list] = None) -> Optional["Credentials"]: creds = Credentials.from_authorized_user_file(str(CREDS_PATH), effective_scopes) json_handler.log_operation("credentials_loaded", {"source": str(CREDS_PATH)}) return creds - except Exception: + except Exception as e: + logger.error(f"Failed to load credentials from {CREDS_PATH}: {e}") return None @@ -113,7 +118,8 @@ def refresh_credentials(creds: "Credentials") -> bool: creds.refresh(Request()) _save_credentials(creds) return True - except Exception: + except Exception as e: + logger.error(f"Failed to refresh credentials: {e}") return False @@ -150,7 +156,8 @@ def run_oauth_flow( creds = flow.run_local_server(port=port, open_browser=open_browser) _save_credentials(creds) return creds - except Exception: + except Exception as e: + logger.error(f"OAuth flow failed: {e}") return None diff --git a/src/aipass/api/apps/handlers/google/service_factory.py b/src/aipass/api/apps/handlers/google/service_factory.py index 7b97a356..54f1b925 100644 --- a/src/aipass/api/apps/handlers/google/service_factory.py +++ b/src/aipass/api/apps/handlers/google/service_factory.py @@ -5,6 +5,7 @@ # Created: 2026-03-14 # Modified: 2026-03-14 # ============================================= +# pyright: reportMissingImports=false, reportOptionalCall=false """ Google API Service Factory @@ -30,6 +31,7 @@ Usage: from typing import Optional +from aipass.prax import logger from aipass.api.apps.handlers.google import auth as auth from aipass.api.apps.handlers.json import json_handler @@ -40,7 +42,8 @@ from aipass.api.apps.handlers.json import json_handler try: from googleapiclient.discovery import build GOOGLE_BUILD_AVAILABLE = True -except ImportError: +except ImportError as e: + logger.warning(f"Google API client library not available: {e}") GOOGLE_BUILD_AVAILABLE = False build = None # type: ignore[assignment] @@ -78,7 +81,8 @@ def build_service( service = build(service_name, version, credentials=creds) json_handler.log_operation("build_service", {"service": service_name, "version": version}) return service - except Exception: + except Exception as e: + logger.error(f"Failed to build Google {service_name} service: {e}") return None @@ -118,5 +122,6 @@ def build_thread_safe_service( try: return build(service_name, version, credentials=creds) - except Exception: + except Exception as e: + logger.error(f"Failed to build thread-safe Google {service_name} service: {e}") return None diff --git a/src/aipass/api/apps/handlers/json/json_handler.py b/src/aipass/api/apps/handlers/json/json_handler.py index a48d5fb2..4e06e0ef 100755 --- a/src/aipass/api/apps/handlers/json/json_handler.py +++ b/src/aipass/api/apps/handlers/json/json_handler.py @@ -13,6 +13,9 @@ from typing import Dict, Any, Optional import sys import inspect +# Logging +from aipass.prax import logger + # Infrastructure # Constants — package-relative paths @@ -42,7 +45,8 @@ def _get_caller_module_name() -> str: # Fallback return "unknown" - except Exception: + except Exception as e: + logger.warning(f"Failed to detect module name: {e}") return "unknown" @@ -118,8 +122,8 @@ def ensure_json_exists(module_name: str, json_type: str) -> bool: return True else: pass # Corrupted - regenerating - except Exception: - pass # Unreadable - regenerating + except Exception as e: + logger.warning(f"Unreadable JSON at {json_path}, regenerating: {e}") template = _create_default(json_type, module_name) @@ -138,7 +142,8 @@ def load_json(module_name: str, json_type: str) -> Optional[Any]: try: with open(json_path, 'r', encoding='utf-8') as f: return json.load(f) - except Exception: + except Exception as e: + logger.error(f"Failed to load JSON from {json_path}: {e}") return None @@ -156,7 +161,8 @@ def save_json(module_name: str, json_type: str, data: Any) -> bool: with open(json_path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) return True - except Exception: + except Exception as e: + logger.error(f"Failed to save JSON to {json_path}: {e}") return False diff --git a/src/aipass/api/apps/handlers/openrouter/caller.py b/src/aipass/api/apps/handlers/openrouter/caller.py index d486908d..cc4f7ee3 100644 --- a/src/aipass/api/apps/handlers/openrouter/caller.py +++ b/src/aipass/api/apps/handlers/openrouter/caller.py @@ -27,6 +27,9 @@ from pathlib import Path import inspect from typing import Dict, Any, Optional, Tuple +# Logging +from aipass.prax import logger + # JSON handler from aipass.api.apps.handlers.json import json_handler @@ -76,11 +79,11 @@ def get_caller_info() -> Optional[Dict[str, Any]]: json_handler.log_operation("caller_detected", {"caller": result.get("caller_name"), "category": "skills"}) return result - # logger.info(f"[{MODULE_NAME}] Could not detect caller from stack trace") + logger.info(f"[{MODULE_NAME}] Could not detect caller from stack trace") return None except Exception as e: - # logger.error(f"[{MODULE_NAME}] Caller detection failed: {e}") + logger.error(f"Caller detection failed: {e}") return None @@ -124,17 +127,17 @@ def get_json_folder_path(caller: str) -> Optional[Path]: base_path = _PACKAGE_ROOT / "skills" / f"skills_{skills_category}" json_folder = base_path / f"{skills_category}_json" else: - # logger.info(f"[{MODULE_NAME}] Cannot parse skills category from: {caller}") + logger.info(f"[{MODULE_NAME}] Cannot parse skills category from: {caller}") return None else: - # logger.info(f"[{MODULE_NAME}] Unknown caller pattern: {caller}") + logger.info(f"[{MODULE_NAME}] Unknown caller pattern: {caller}") return None - # logger.info(f"[{MODULE_NAME}] Resolved JSON folder for {caller}: {json_folder}") + logger.info(f"[{MODULE_NAME}] Resolved JSON folder for {caller}: {json_folder}") return json_folder except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to determine JSON folder for {caller}: {e}") + logger.error(f"Failed to determine JSON folder for {caller}: {e}") return None @@ -153,7 +156,7 @@ def detect_caller_category(caller_path: Path) -> str: return "unknown" except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to detect category for {caller_path}: {e}") + logger.error(f"Failed to detect category for {caller_path}: {e}") return "unknown" @@ -169,7 +172,7 @@ def _detect_flow_caller(frame_path: Path) -> Dict[str, Any]: json_folder_path = flow_path / "flow_json" caller_name = frame_path.stem - # logger.info(f"[{MODULE_NAME}] Detected flow caller: {caller_name}") + logger.info(f"[{MODULE_NAME}] Detected flow caller: {caller_name}") return { "caller_name": caller_name, @@ -180,7 +183,7 @@ def _detect_flow_caller(frame_path: Path) -> Dict[str, Any]: } except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to detect flow caller: {e}") + logger.error(f"Failed to detect flow caller: {e}") return _create_fallback_info(frame_path) @@ -192,7 +195,7 @@ def _detect_prax_caller(frame_path: Path) -> Dict[str, Any]: json_folder_path = prax_path / "prax_json" caller_name = frame_path.stem - # logger.info(f"[{MODULE_NAME}] Detected prax caller: {caller_name}") + logger.info(f"[{MODULE_NAME}] Detected prax caller: {caller_name}") return { "caller_name": caller_name, @@ -203,7 +206,7 @@ def _detect_prax_caller(frame_path: Path) -> Dict[str, Any]: } except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to detect prax caller: {e}") + logger.error(f"Failed to detect prax caller: {e}") return _create_fallback_info(frame_path) @@ -220,7 +223,7 @@ def _detect_skills_caller(frame_path: Path) -> Dict[str, Any]: json_folder_path = skills_path / f"{category}_json" caller_name = frame_path.stem - # logger.info(f"[{MODULE_NAME}] Detected skills caller: {caller_name} (category: {category})") + logger.info(f"[{MODULE_NAME}] Detected skills caller: {caller_name} (category: {category})") return { "caller_name": caller_name, @@ -231,11 +234,11 @@ def _detect_skills_caller(frame_path: Path) -> Dict[str, Any]: "detection_method": "stack" } - # logger.info(f"[{MODULE_NAME}] Could not find skills directory in path: {frame_path}") + logger.info(f"[{MODULE_NAME}] Could not find skills directory in path: {frame_path}") return _create_fallback_info(frame_path) except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to detect skills caller: {e}") + logger.error(f"Failed to detect skills caller: {e}") return _create_fallback_info(frame_path) @@ -244,7 +247,7 @@ def _create_fallback_info(frame_path: Path) -> Dict[str, Any]: caller_name = frame_path.stem category = detect_caller_category(frame_path) - # logger.info(f"[{MODULE_NAME}] Using fallback detection for: {caller_name}") + logger.info(f"[{MODULE_NAME}] Using fallback detection for: {caller_name}") return { "caller_name": caller_name, @@ -265,26 +268,26 @@ def validate_caller_info(caller_info: Dict[str, Any]) -> bool: required_fields = ["caller_name", "caller_path", "category", "detection_method"] for field in required_fields: if field not in caller_info: - # logger.info(f"[{MODULE_NAME}] Missing required field: {field}") + logger.info(f"[{MODULE_NAME}] Missing required field: {field}") return False if not caller_info["caller_name"]: - # logger.info(f"[{MODULE_NAME}] Caller name is empty") + logger.info(f"[{MODULE_NAME}] Caller name is empty") return False if not isinstance(caller_info["caller_path"], Path): - # logger.info(f"[{MODULE_NAME}] Caller path is not a Path object") + logger.info(f"[{MODULE_NAME}] Caller path is not a Path object") return False valid_categories = ["flow", "prax", "skills", "unknown"] if caller_info["category"] not in valid_categories: - # logger.info(f"[{MODULE_NAME}] Invalid category: {caller_info['category']}") + logger.info(f"[{MODULE_NAME}] Invalid category: {caller_info['category']}") return False return True except Exception as e: - # logger.error(f"[{MODULE_NAME}] Validation failed: {e}") + logger.error(f"Validation failed: {e}") return False @@ -294,7 +297,6 @@ def validate_caller_info(caller_info: Dict[str, Any]) -> bool: def _initialize(): """Initialize caller detection module.""" - # logger.info(f"[{MODULE_NAME}] Caller detection handler loaded (v{MODULE_VERSION})") - pass + logger.info(f"[{MODULE_NAME}] Caller detection handler loaded (v{MODULE_VERSION})") _initialize() diff --git a/src/aipass/api/apps/handlers/openrouter/client.py b/src/aipass/api/apps/handlers/openrouter/client.py index e467d837..3a89736c 100644 --- a/src/aipass/api/apps/handlers/openrouter/client.py +++ b/src/aipass/api/apps/handlers/openrouter/client.py @@ -48,8 +48,8 @@ from aipass.prax import logger try: from openai import OpenAI OPENAI_AVAILABLE = True -except ImportError: - # logger.error("OpenAI SDK not available. Install with: pip install openai") +except ImportError as e: + logger.error(f"OpenAI SDK not available. Install with: pip install openai: {e}") OpenAI = None # type: ignore[assignment,misc] OPENAI_AVAILABLE = False @@ -121,7 +121,7 @@ def create_client(api_key: str, base_url: str = OPENROUTER_BASE_URL, timeout: in default_headers=OPENROUTER_HEADERS ) - # logger.info(f"Created OpenRouter client - base_url: {base_url}, timeout: {timeout}s") + logger.info(f"Created OpenRouter client - base_url: {base_url}, timeout: {timeout}s") json_handler.log_operation("client_initialized", {"base_url": base_url, "timeout": timeout}) return client @@ -155,7 +155,7 @@ def get_cached_client(api_key: str, base_url: str = OPENROUTER_BASE_URL, timeout cached_client = _client_cache[api_key] # Verify cached client is still valid if cached_client and cached_client.api_key == api_key: - # logger.info("Using cached OpenRouter client") + logger.info("Using cached OpenRouter client") return cached_client # Create new client @@ -169,10 +169,10 @@ def get_cached_client(api_key: str, base_url: str = OPENROUTER_BASE_URL, timeout # Remove oldest client (first key in dict) oldest_key = next(iter(_client_cache)) del _client_cache[oldest_key] - # logger.info(f"Removed oldest cached client - cache limit: {MAX_CACHED_CLIENTS}") + logger.info(f"Removed oldest cached client - cache limit: {MAX_CACHED_CLIENTS}") _client_cache[api_key] = client - # logger.info("Cached new OpenRouter client") + logger.info("Cached new OpenRouter client") return client @@ -257,7 +257,7 @@ def extract_response(response: Any) -> Optional[Dict[str, Any]]: content = response.choices[0].message.content if not content: - # logger.warning("Response has no content") + logger.warning("Response has no content") return None # Extract metadata @@ -268,11 +268,11 @@ def extract_response(response: Any) -> Optional[Dict[str, Any]]: "finish_reason": response.choices[0].finish_reason if hasattr(response.choices[0], 'finish_reason') else None } - # logger.info(f"Extracted response - length: {len(content)} chars, id: {result['id']}") + logger.info(f"Extracted response - length: {len(content)} chars, id: {result['id']}") return result except Exception as e: - # logger.error(f"Failed to extract response: {e}") + logger.error(f"Failed to extract response: {e}") return None @@ -311,16 +311,16 @@ def get_response(prompt: str, caller: Optional[str] = None, model: Optional[str] caller_info = get_caller_info() if caller_info and caller_info.get("caller_name"): caller = caller_info["caller_name"] - # logger.info(f"Auto-detected caller: {caller}") + logger.info(f"Auto-detected caller: {caller}") else: - # logger.warning("Could not detect caller - using 'unknown'") + logger.warning("Could not detect caller - using 'unknown'") caller = "unknown" # Step 1b: Ensure caller has config (auto-provision if missing) try: ensure_caller_config(caller) - except Exception: - pass # Provisioning is best-effort — don't block the API call + except Exception as e: + logger.warning(f"Caller config provisioning failed (non-blocking): {e}") # Step 2: Require model from caller - no defaults if not model: @@ -362,11 +362,9 @@ def get_response(prompt: str, caller: Optional[str] = None, model: Optional[str] try: track_usage(result["id"], caller if caller else "unknown", model, api_key) except Exception as e: - # logger.warning(f"Usage tracking failed: {e}") - # Don't fail the request if tracking fails - pass + logger.warning(f"Usage tracking failed: {e}") - # logger.info(f"Successfully got response - caller: {caller}, model: {model}, length: {len(result['content'])} chars") + logger.info(f"Successfully got response - caller: {caller}, model: {model}, length: {len(result['content'])} chars") return result @@ -382,7 +380,6 @@ def clear_client_cache() -> None: global _client_cache count = len(_client_cache) _client_cache.clear() - # logger.info(f"Cleared {count} cached clients") logger.info(f"Cleared {count} cached OpenRouter clients") diff --git a/src/aipass/api/apps/handlers/openrouter/models.py b/src/aipass/api/apps/handlers/openrouter/models.py index 72305be9..05396e81 100644 --- a/src/aipass/api/apps/handlers/openrouter/models.py +++ b/src/aipass/api/apps/handlers/openrouter/models.py @@ -74,7 +74,7 @@ def get_available_models(api_key: Optional[str] = None) -> List[Dict]: api_key = get_api_key("openrouter") if not api_key: - # logger.info(f"[{MODULE_NAME}] No API key available for OpenRouter") + logger.info(f"[{MODULE_NAME}] No API key available for OpenRouter") logger.warning("No OpenRouter API key found") return [] @@ -82,15 +82,15 @@ def get_available_models(api_key: Optional[str] = None) -> List[Dict]: models = fetch_models_from_api(api_key) if models: - # logger.info(f"[{MODULE_NAME}] Fetched {len(models)} models from OpenRouter") + logger.info(f"[{MODULE_NAME}] Fetched {len(models)} models from OpenRouter") json_handler.log_operation("models_listed", {"count": len(models)}) return models else: - # logger.info(f"[{MODULE_NAME}] No models returned from API") + logger.info(f"[{MODULE_NAME}] No models returned from API") return [] except Exception as e: - # logger.info(f"[{MODULE_NAME}] Failed to get available models: {e}") + logger.info(f"[{MODULE_NAME}] Failed to get available models: {e}") logger.error(f"Error fetching models: {e}") return [] @@ -123,11 +123,11 @@ def get_free_models(api_key: Optional[str] = None) -> List[Dict]: # Filter for free models free_models = filter_by_pricing(all_models, max_cost=0.0) - # logger.info(f"[{MODULE_NAME}] Found {len(free_models)} free models") + logger.info(f"[{MODULE_NAME}] Found {len(free_models)} free models") return free_models except Exception as e: - # logger.info(f"[{MODULE_NAME}] Failed to get free models: {e}") + logger.info(f"[{MODULE_NAME}] Failed to get free models: {e}") logger.error(f"Error fetching free models: {e}") return [] @@ -156,7 +156,7 @@ def fetch_models_from_api(api_key: str) -> List[Dict]: } # Make API request - # logger.info(f"[{MODULE_NAME}] Requesting models from OpenRouter API") + logger.info(f"[{MODULE_NAME}] Requesting models from OpenRouter API") response = requests.get( OPENROUTER_API_URL, headers=headers, @@ -165,7 +165,7 @@ def fetch_models_from_api(api_key: str) -> List[Dict]: # Check response status if response.status_code != 200: - # logger.info(f"[{MODULE_NAME}] API request failed with status {response.status_code}") + logger.info(f"[{MODULE_NAME}] API request failed with status {response.status_code}") logger.error(f"OpenRouter API error: {response.status_code}") return [] @@ -175,29 +175,29 @@ def fetch_models_from_api(api_key: str) -> List[Dict]: # Extract models from response if "data" in data and isinstance(data["data"], list): models = data["data"] - # logger.info(f"[{MODULE_NAME}] Successfully parsed {len(models)} models") + logger.info(f"[{MODULE_NAME}] Successfully parsed {len(models)} models") return models else: - # logger.info(f"[{MODULE_NAME}] Invalid response format - no 'data' field") + logger.info(f"[{MODULE_NAME}] Invalid response format - no 'data' field") return [] except requests.exceptions.Timeout: - # logger.info(f"[{MODULE_NAME}] API request timeout after {DEFAULT_TIMEOUT}s") + logger.info(f"[{MODULE_NAME}] API request timeout after {DEFAULT_TIMEOUT}s") logger.error("Request timeout - OpenRouter API not responding") return [] except requests.exceptions.RequestException as e: - # logger.info(f"[{MODULE_NAME}] Network error: {e}") + logger.info(f"[{MODULE_NAME}] Network error: {e}") logger.error(f"Network error: {e}") return [] except ValueError as e: - # logger.info(f"[{MODULE_NAME}] JSON parse error: {e}") + logger.info(f"[{MODULE_NAME}] JSON parse error: {e}") logger.error("Invalid JSON response from API") return [] except Exception as e: - # logger.info(f"[{MODULE_NAME}] Unexpected error fetching models: {e}") + logger.info(f"[{MODULE_NAME}] Unexpected error fetching models: {e}") logger.error(f"Error: {e}") return [] @@ -232,19 +232,20 @@ def filter_by_pricing(models: List[Dict], max_cost: float = 0.0) -> List[Dict]: try: prompt_cost = float(pricing.get("prompt", "0")) completion_cost = float(pricing.get("completion", "0")) - except (ValueError, TypeError): + except (ValueError, TypeError) as e: # Skip models with invalid pricing data + logger.warning(f"Skipping model with invalid pricing data: {e}") continue # Check if both costs are at or below threshold if prompt_cost <= max_cost and completion_cost <= max_cost: filtered.append(model) - # logger.info(f"[{MODULE_NAME}] Filtered {len(filtered)}/{len(models)} models at max_cost={max_cost}") + logger.info(f"[{MODULE_NAME}] Filtered {len(filtered)}/{len(models)} models at max_cost={max_cost}") return filtered except Exception as e: - # logger.info(f"[{MODULE_NAME}] Error filtering models: {e}") + logger.error(f"Error filtering models: {e}") return [] @@ -264,14 +265,14 @@ def get_model_by_id(model_id: str, api_key: Optional[str] = None) -> Optional[Di for model in all_models: if model.get("id") == model_id: - # logger.info(f"[{MODULE_NAME}] Found model: {model_id}") + logger.info(f"[{MODULE_NAME}] Found model: {model_id}") return model - # logger.info(f"[{MODULE_NAME}] Model not found: {model_id}") + logger.info(f"[{MODULE_NAME}] Model not found: {model_id}") return None except Exception as e: - # logger.info(f"[{MODULE_NAME}] Error finding model {model_id}: {e}") + logger.error(f"Error finding model {model_id}: {e}") return None @@ -314,7 +315,7 @@ def extract_model_metadata(model: Dict) -> Dict: return metadata except Exception as e: - # logger.info(f"[{MODULE_NAME}] Error extracting metadata: {e}") + logger.error(f"Error extracting metadata: {e}") return {} @@ -331,7 +332,7 @@ def list_model_ids(models: List[Dict]) -> List[str]: try: return [model.get("id", "") for model in models if model.get("id")] except Exception as e: - # logger.info(f"[{MODULE_NAME}] Error extracting IDs: {e}") + logger.error(f"Error extracting model IDs: {e}") return [] diff --git a/src/aipass/api/apps/handlers/openrouter/provision.py b/src/aipass/api/apps/handlers/openrouter/provision.py index 3ad62896..78eb4f89 100644 --- a/src/aipass/api/apps/handlers/openrouter/provision.py +++ b/src/aipass/api/apps/handlers/openrouter/provision.py @@ -61,7 +61,7 @@ def read_json(file_path: Path) -> Optional[Dict[str, Any]]: with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: - # logger.error(f"Failed to read {file_path}: {e}") + logger.error(f"Failed to read {file_path}: {e}") return None @@ -84,7 +84,7 @@ def write_json(file_path: Path, data: Dict[str, Any]) -> bool: return True except Exception as e: - # logger.error(f"Failed to write {file_path}: {e}") + logger.error(f"Failed to write {file_path}: {e}") return False @@ -166,7 +166,7 @@ def provision_json_folder(json_folder: Path) -> bool: return True json_folder.mkdir(parents=True, exist_ok=True) - # logger.info(f"Created JSON folder: {json_folder}") + logger.info(f"Created JSON folder: {json_folder}") logger.info(f"Created JSON folder: {json_folder}") return True @@ -205,7 +205,7 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]: # logger.error(f"Failed to write config for {caller}") return {} - # logger.info(f"Created API config for {caller}: {config_file}") + logger.info(f"Created API config for {caller}: {config_file}") logger.info(f"Created config: {config_file.name}") # Create data file @@ -213,7 +213,7 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]: data = get_default_caller_data() if write_json(data_file, data): - # logger.info(f"Created data file for {caller}: {data_file}") + logger.info(f"Created data file for {caller}: {data_file}") logger.info(f"Created data: {data_file.name}") # Create log file @@ -221,7 +221,7 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]: log_data = get_default_caller_log() if write_json(log_file, log_data): - # logger.info(f"Created log file for {caller}: {log_file}") + logger.info(f"Created log file for {caller}: {log_file}") logger.info(f"Created log: {log_file.name}") logger.info(f"Auto-provisioned OpenRouter config for '{caller}'") @@ -256,9 +256,8 @@ def ensure_caller_config(caller: str | None = None) -> Dict[str, Any]: detected_caller, json_folder = detect_caller_from_stack() if detected_caller: caller = detected_caller - # logger.info(f"Auto-detected caller: {caller}") + logger.info(f"Auto-detected caller: {caller}") else: - # logger.warning("Could not detect caller from stack") logger.warning("Could not detect caller module") return {} @@ -276,11 +275,10 @@ def ensure_caller_config(caller: str | None = None) -> Dict[str, Any]: if config_file.exists(): config = read_json(config_file) if config: - # logger.info(f"Using existing config for {caller}") + logger.info(f"Using existing config for {caller}") return config else: - # logger.warning(f"Config file corrupted for {caller}, regenerating") - logger.warning("Corrupted config, regenerating...") + logger.warning(f"Config file corrupted for {caller}, regenerating") # Create new config return create_caller_config(caller, json_folder) diff --git a/src/aipass/api/apps/handlers/usage/aggregation.py b/src/aipass/api/apps/handlers/usage/aggregation.py index d1406b91..e5f2b8b6 100644 --- a/src/aipass/api/apps/handlers/usage/aggregation.py +++ b/src/aipass/api/apps/handlers/usage/aggregation.py @@ -29,6 +29,9 @@ from typing import Dict, Any, List, Optional # Standard library for JSON operations import json +# Logging +from aipass.prax import logger + # JSON handler from aipass.api.apps.handlers.json import json_handler @@ -62,14 +65,14 @@ def get_caller_usage(caller: str) -> Dict[str, Any]: # Load usage data from JSON data_path = API_JSON_DIR / DATA_FILE if not data_path.exists(): - # logger.info(f"[{MODULE_NAME}] No usage data file found") + logger.info(f"[{MODULE_NAME}] No usage data file found") return {} with open(data_path, 'r', encoding='utf-8') as f: data = json.load(f) if not data or "data" not in data: - # logger.info(f"[{MODULE_NAME}] No usage data available") + logger.info(f"[{MODULE_NAME}] No usage data available") return {} # Extract caller-specific data @@ -77,14 +80,14 @@ def get_caller_usage(caller: str) -> Dict[str, Any]: caller_data = usage_by_caller.get(caller, {}) if not caller_data: - # logger.info(f"[{MODULE_NAME}] No usage data found for caller: {caller}") + logger.info(f"[{MODULE_NAME}] No usage data found for caller: {caller}") return {} - # logger.info(f"[{MODULE_NAME}] Retrieved usage stats for {caller}: {caller_data.get('requests', 0)} requests") + logger.info(f"[{MODULE_NAME}] Retrieved usage stats for {caller}: {caller_data.get('requests', 0)} requests") return caller_data except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to get caller usage for {caller}: {e}") + logger.error(f"[{MODULE_NAME}] Failed to get caller usage for {caller}: {e}") return {} @@ -103,28 +106,28 @@ def get_session_summary(session_id: Optional[str] = None) -> Dict[str, Any]: # Load usage data from JSON data_path = API_JSON_DIR / DATA_FILE if not data_path.exists(): - # logger.info(f"[{MODULE_NAME}] No session data file found") + logger.info(f"[{MODULE_NAME}] No session data file found") return {} with open(data_path, 'r', encoding='utf-8') as f: data = json.load(f) if not data or "data" not in data: - # logger.info(f"[{MODULE_NAME}] No session data available") + logger.info(f"[{MODULE_NAME}] No session data available") return {} # Extract session summary session_data = data["data"].get("current_session", {}) if not session_data: - # logger.info(f"[{MODULE_NAME}] No session summary found") + logger.info(f"[{MODULE_NAME}] No session summary found") return {} - # logger.info(f"[{MODULE_NAME}] Retrieved session summary: {session_data.get('total_requests', 0)} requests") + logger.info(f"[{MODULE_NAME}] Retrieved session summary: {session_data.get('total_requests', 0)} requests") return session_data except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to get session summary: {e}") + logger.error(f"[{MODULE_NAME}] Failed to get session summary: {e}") return {} @@ -147,14 +150,14 @@ def get_daily_usage(date: Optional[str] = None) -> Dict[str, Any]: # Load usage data from JSON data_path = API_JSON_DIR / DATA_FILE if not data_path.exists(): - # logger.info(f"[{MODULE_NAME}] No daily usage data file found") + logger.info(f"[{MODULE_NAME}] No daily usage data file found") return {} with open(data_path, 'r', encoding='utf-8') as f: data = json.load(f) if not data or "data" not in data: - # logger.info(f"[{MODULE_NAME}] No daily usage data available") + logger.info(f"[{MODULE_NAME}] No daily usage data available") return {} # Extract daily totals @@ -162,14 +165,14 @@ def get_daily_usage(date: Optional[str] = None) -> Dict[str, Any]: daily_data = daily_totals.get(date, {}) if not daily_data: - # logger.info(f"[{MODULE_NAME}] No usage data found for date: {date}") + logger.info(f"[{MODULE_NAME}] No usage data found for date: {date}") return {} - # logger.info(f"[{MODULE_NAME}] Retrieved daily usage for {date}: {daily_data.get('requests', 0)} requests") + logger.info(f"[{MODULE_NAME}] Retrieved daily usage for {date}: {daily_data.get('requests', 0)} requests") return daily_data except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to get daily usage for {date}: {e}") + logger.error(f"[{MODULE_NAME}] Failed to get daily usage for {date}: {e}") return {} @@ -185,7 +188,7 @@ def calculate_totals(usage_data: List[Dict]) -> Dict[str, float]: """ try: if not usage_data: - # logger.info(f"[{MODULE_NAME}] No usage data provided for totals calculation") + logger.info(f"[{MODULE_NAME}] No usage data provided for totals calculation") return { "total_cost": 0.0, "total_tokens": 0, @@ -224,12 +227,12 @@ def calculate_totals(usage_data: List[Dict]) -> Dict[str, float]: "total_latency": total_latency } - # logger.info(f"[{MODULE_NAME}] Calculated totals: {result['total_requests']} requests, ${result['total_cost']:.6f}") + logger.info(f"[{MODULE_NAME}] Calculated totals: {result['total_requests']} requests, ${result['total_cost']:.6f}") json_handler.log_operation("usage_aggregated", {"total_requests": result["total_requests"], "total_cost": result["total_cost"]}) return result except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to calculate totals: {e}") + logger.error(f"[{MODULE_NAME}] Failed to calculate totals: {e}") return { "total_cost": 0.0, "total_tokens": 0, @@ -253,14 +256,14 @@ def get_model_breakdown(caller: Optional[str] = None) -> Dict[str, Dict[str, int try: data_path = API_JSON_DIR / DATA_FILE if not data_path.exists(): - # logger.info(f"[{MODULE_NAME}] No model breakdown data file found") + logger.info(f"[{MODULE_NAME}] No model breakdown data file found") return {} with open(data_path, 'r', encoding='utf-8') as f: data = json.load(f) if not data or "data" not in data: - # logger.info(f"[{MODULE_NAME}] No model breakdown data available") + logger.info(f"[{MODULE_NAME}] No model breakdown data available") return {} model_stats = {} @@ -281,9 +284,9 @@ def get_model_breakdown(caller: Optional[str] = None) -> Dict[str, Dict[str, int model_stats[model] = {"requests": 0} model_stats[model]["requests"] += count - # logger.info(f"[{MODULE_NAME}] Retrieved model breakdown: {len(model_stats)} models") + logger.info(f"[{MODULE_NAME}] Retrieved model breakdown: {len(model_stats)} models") return model_stats except Exception as e: - # logger.error(f"[{MODULE_NAME}] Failed to get model breakdown: {e}") + logger.error(f"[{MODULE_NAME}] Failed to get model breakdown: {e}") return {} diff --git a/src/aipass/api/apps/handlers/usage/cleanup.py b/src/aipass/api/apps/handlers/usage/cleanup.py index b04e26fd..26f9f2de 100644 --- a/src/aipass/api/apps/handlers/usage/cleanup.py +++ b/src/aipass/api/apps/handlers/usage/cleanup.py @@ -37,7 +37,7 @@ def _read_json(file_path: Path) -> Optional[Dict]: with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: - # logger.error(f"Failed to read JSON from {file_path}: {e}") + logger.error(f"Failed to read JSON from {file_path}: {e}") return None @@ -49,7 +49,7 @@ def _write_json(file_path: Path, data: Dict) -> bool: json.dump(data, f, indent=2, ensure_ascii=False) return True except Exception as e: - # logger.error(f"Failed to write JSON to {file_path}: {e}") + logger.error(f"Failed to write JSON to {file_path}: {e}") return False @@ -91,7 +91,7 @@ def cleanup_old_data(data_file_path: Path, retention_days: int = 30) -> int: data["timestamp"] = datetime.now().isoformat() _write_json(data_file_path, data) - # logger.info(f"Cleaned up {len(old_generations)} generation entries") + logger.info(f"Cleaned up {len(old_generations)} generation entries") logger.info(f"Cleaned up {len(old_generations)} generation entries older than {retention_days} days") json_handler.log_operation("usage_cleanup", {"generations_removed": len(old_generations), "retention_days": retention_days}) @@ -118,7 +118,8 @@ def _identify_old_generations(generation_tracking: Dict, cutoff_date: datetime) if gen_date < cutoff_date: old_generations.append(gen_id) - except (ValueError, TypeError): + except (ValueError, TypeError) as e: + logger.warning(f"Invalid timestamp for generation {gen_id}, marking for cleanup: {e}") old_generations.append(gen_id) return old_generations @@ -141,7 +142,8 @@ def cleanup_daily_totals(data_file_path: Path, retention_days: int = 90) -> int: date_obj = datetime.fromisoformat(date_str).date() if date_obj < cutoff_date: old_dates.append(date_str) - except (ValueError, TypeError): + except (ValueError, TypeError) as e: + logger.warning(f"Invalid date format '{date_str}', marking for cleanup: {e}") old_dates.append(date_str) if not old_dates: @@ -155,7 +157,7 @@ def cleanup_daily_totals(data_file_path: Path, retention_days: int = 90) -> int: data["timestamp"] = datetime.now().isoformat() _write_json(data_file_path, data) - # logger.info(f"Cleaned up {len(old_dates)} daily total entries") + logger.info(f"Cleaned up {len(old_dates)} daily total entries") logger.info(f"Cleaned up {len(old_dates)} daily total entries older than {retention_days} days") return len(old_dates) @@ -175,7 +177,7 @@ def auto_cleanup(data_file_path: Path, config: Optional[Dict] = None) -> Dict[st generations_removed = cleanup_old_data(data_file_path, gen_retention) daily_totals_removed = cleanup_daily_totals(data_file_path, daily_retention) - # logger.info(f"Auto cleanup: {generations_removed} generations, {daily_totals_removed} daily totals removed") + logger.info(f"Auto cleanup: {generations_removed} generations, {daily_totals_removed} daily totals removed") return { "generations_removed": generations_removed, @@ -225,7 +227,7 @@ def get_cleanup_stats(data_file_path: Path) -> Dict[str, int]: } except Exception as e: - # logger.error(f"Failed to get cleanup stats: {e}") + logger.error(f"Failed to get cleanup stats: {e}") return empty_stats @@ -234,5 +236,6 @@ def _is_old_date(date_str: str, cutoff_date) -> bool: try: date_obj = datetime.fromisoformat(date_str).date() return date_obj < cutoff_date - except (ValueError, TypeError): + except (ValueError, TypeError) as e: + logger.warning(f"Invalid date format '{date_str}' in _is_old_date: {e}") return True diff --git a/src/aipass/api/apps/handlers/usage/tracking.py b/src/aipass/api/apps/handlers/usage/tracking.py index a6c659a7..14c46ba3 100644 --- a/src/aipass/api/apps/handlers/usage/tracking.py +++ b/src/aipass/api/apps/handlers/usage/tracking.py @@ -29,6 +29,9 @@ import time from datetime import datetime from typing import Dict, Any, Optional +# Logging +from aipass.prax import logger + # JSON handler from aipass.api.apps.handlers.json import json_handler @@ -82,7 +85,7 @@ def track_usage(generation_id: str, caller: str, model: str = "unknown", api_key from aipass.api.apps.handlers.auth.keys import get_api_key api_key = get_api_key("openrouter") except Exception as e: - # Failed to load API key + logger.error(f"[{MODULE_NAME}] Failed to load API key: {e}") return {"success": False, "error": "No API key available"} if not api_key: @@ -109,7 +112,7 @@ def track_usage(generation_id: str, caller: str, model: str = "unknown", api_key return {"success": False, "error": "Failed to store usage data"} except Exception as e: - # Usage tracking failed + logger.error(f"[{MODULE_NAME}] Usage tracking failed: {e}") return {"success": False, "error": str(e)} @@ -176,20 +179,20 @@ def get_generation_metrics(generation_id: str, api_key: str) -> Optional[Dict[st # OpenRouter API returned non-200 status return None - except requests.exceptions.Timeout: - # Request timeout querying generation + except requests.exceptions.Timeout as e: + logger.warning(f"[{MODULE_NAME}] Request timeout querying generation: {e}") return None except requests.exceptions.RequestException as e: - # Request error querying generation + logger.error(f"[{MODULE_NAME}] Request error querying generation: {e}") return None except (ValueError, KeyError) as e: - # Error parsing metrics + logger.error(f"[{MODULE_NAME}] Error parsing metrics: {e}") return None except Exception as e: - # Unexpected error getting generation metrics + logger.error(f"[{MODULE_NAME}] Unexpected error getting generation metrics: {e}") return None @@ -304,7 +307,7 @@ def store_usage_data(caller: str, model: str, generation_id: str, metrics: Dict[ return True except Exception as e: - # Failed to store usage data + logger.error(f"[{MODULE_NAME}] Failed to store usage data: {e}") return False @@ -330,5 +333,5 @@ def load_usage_data() -> Dict[str, Any]: return data_wrapper.get("data", {}) except Exception as e: - # Failed to load usage data + logger.error(f"[{MODULE_NAME}] Failed to load usage data: {e}") return {} diff --git a/src/aipass/api/apps/modules/api_key.py b/src/aipass/api/apps/modules/api_key.py index e0d54250..3600e151 100644 --- a/src/aipass/api/apps/modules/api_key.py +++ b/src/aipass/api/apps/modules/api_key.py @@ -226,6 +226,6 @@ if __name__ == "__main__": console.print() console.print(f"[red]Unknown command: {command}[/red]") console.print() - console.print("Run [dim]python3 api_key.py --help[/dim] for available commands") + console.print("Run [dim]drone @api --help[/dim] for available commands") console.print() sys.exit(1) diff --git a/src/aipass/api/apps/modules/google_client.py b/src/aipass/api/apps/modules/google_client.py index 98196fb4..1e669107 100644 --- a/src/aipass/api/apps/modules/google_client.py +++ b/src/aipass/api/apps/modules/google_client.py @@ -350,8 +350,6 @@ if __name__ == "__main__": console.print() console.print(f"[red]Unknown command: {command}[/red]") console.print() - console.print( - "Run [dim]python3 google_client.py --help[/dim] for available commands" - ) + console.print("Run [dim]drone @api --help[/dim] for available commands") console.print() sys.exit(1) diff --git a/src/aipass/api/apps/modules/openrouter_client.py b/src/aipass/api/apps/modules/openrouter_client.py index 0c2bc8c6..560e20ff 100644 --- a/src/aipass/api/apps/modules/openrouter_client.py +++ b/src/aipass/api/apps/modules/openrouter_client.py @@ -281,6 +281,7 @@ def check_status(): import openai # noqa: F401 console.print(f" [cyan]OpenAI SDK:[/cyan] [green]available[/green]") except ImportError: + logger.warning("OpenAI SDK not installed") console.print(f" [cyan]OpenAI SDK:[/cyan] [red]missing[/red]") # Client cache stats @@ -343,6 +344,6 @@ if __name__ == "__main__": console.print() console.print(f"[red]Unknown command: {command}[/red]") console.print() - console.print("Run [dim]python3 openrouter_client.py --help[/dim] for available commands") + console.print("Run [dim]drone @api --help[/dim] for available commands") console.print() sys.exit(1) diff --git a/src/aipass/api/apps/modules/usage_tracker.py b/src/aipass/api/apps/modules/usage_tracker.py index e6d7983e..068447cd 100644 --- a/src/aipass/api/apps/modules/usage_tracker.py +++ b/src/aipass/api/apps/modules/usage_tracker.py @@ -251,7 +251,7 @@ def cleanup_data(args: List[str]): from aipass.trigger.apps.modules.core import trigger trigger.fire('usage_data_cleaned', days=days, data_path=str(data_path)) except ImportError: - pass # Silent fallback + logger.warning("Trigger module not available — skipping event fire") else: error("Cleanup failed") @@ -280,6 +280,6 @@ if __name__ == "__main__": console.print() console.print(f"[red]Unknown command: {command}[/red]") console.print() - console.print("Run [dim]python3 usage_tracker.py --help[/dim] for available commands") + console.print("Run [dim]drone @api --help[/dim] for available commands") console.print() sys.exit(1)