feat(system): seedgo v2 operational, full system audit, 14/15 branches at 99%
Three days of intensive work bringing seedgo to full operational status and driving all branches through comprehensive standards compliance. Seedgo v2.0.0: - 22 checkers active (up from 20), standards pack fully operational - New introspection standard researched from Dev-Pass, FPLAN-0017 open - Bypass system for false positives (.seedgo config) - Standards query and audit commands fully functional System-wide audit (FPLAN-0016): - All 14 auditable branches at 99%+ compliance - CLI imports standardized across all branches (console from cli.apps.modules) - handle_command(command, args) → bool contract added to all modules - print_help() function naming fixed for checker pattern matching - Handler extraction: large modules split, file I/O moved to handler layer - New handlers created across ai_mail, backup, daemon, flow, skills, spawn, seedgo Branch-specific highlights: - ai_mail: email.py split 840→420 lines, 4 new handlers - flow: dplan_flow.py 688→591 lines, 4 new handlers - seedgo: massive restructure — standards moved to handlers/aipass_standards/, old standards/ tree removed, bypass system added, diagnostics module - commons: database module added, CLI imports fixed - skills: 5 handle_commands added, help function renamed - trigger: error reporter handler, handle_command routing - All branches: consistent architecture, clean drone routing Culture doc (CLAUDE.md) added — documents AIPass philosophy, identity, memory system, and collaboration principles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
09e759a8a4
commit
babedd9c64
@@ -2,6 +2,7 @@
|
||||
|
||||
**Purpose:** LLM API access layer with provider abstraction, key management, model routing, and usage tracking.
|
||||
**Module:** `aipass.api`
|
||||
**Last Updated:** 2026-03-08
|
||||
|
||||
---
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
---
|
||||
|
||||
## CLI
|
||||
## Commands / Usage
|
||||
|
||||
```bash
|
||||
drone @api get-key # Retrieve API key for provider
|
||||
@@ -30,8 +31,11 @@ drone @api models # List available models from provider
|
||||
drone @api track # Track API usage metrics
|
||||
drone @api stats # Display API usage statistics
|
||||
drone @api --help # Full help output
|
||||
drone @api --version # Show version
|
||||
```
|
||||
|
||||
Running `drone @api` with no arguments displays module introspection (discovered modules and status).
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
@@ -76,3 +80,7 @@ api/
|
||||
### Provides To
|
||||
- All modules -- LLM API access for any branch that needs model inference
|
||||
- System-wide API key management and credential validation
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: 2026-03-08*
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: main.py - api Branch Orchestrator
|
||||
# Date: 2025-11-08
|
||||
# =================== AIPass ====================
|
||||
# Name: api.py
|
||||
# Description: Entry point CLI for drone @api — LLM client via OpenRouter
|
||||
# Version: 1.0.0
|
||||
# Category: api/entry_point
|
||||
# CODE STANDARDS: Seed v1.0.0
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-08): Initial version - modular architecture
|
||||
# Created: 2026-03-08
|
||||
# Modified: 2026-03-08
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: env.py - .env file operations
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: env.py
|
||||
# Description: .env file operations
|
||||
# Version: 0.2.0
|
||||
# Category: api/handlers
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v0.2.0 (2025-11-16): Extracted from api_connect.py - complete implementation
|
||||
# - v0.1.0 (2025-11-15): Initial handler stub
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Handler layer (standalone functions)
|
||||
# - Uses CLI service for output
|
||||
# - Under 300 lines
|
||||
# ==============================================
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
.env File Handler
|
||||
@@ -36,8 +26,8 @@ import sys
|
||||
# Standard library
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
# CLI services
|
||||
from aipass.cli.apps.modules import console
|
||||
# Logging
|
||||
from aipass.prax import logger
|
||||
|
||||
|
||||
# ==============================================
|
||||
@@ -172,7 +162,7 @@ def create_env_template(provider: str = "openrouter", target_path: Optional[Path
|
||||
# Don't overwrite existing file
|
||||
if env_path.exists():
|
||||
# .env file already exists
|
||||
console.print(f"[yellow]ℹ[/yellow] .env file already exists at {env_path}")
|
||||
logger.info(f".env file already exists at {env_path}")
|
||||
return True
|
||||
|
||||
# Template content based on provider
|
||||
@@ -208,12 +198,12 @@ OPENAI_API_KEY=sk-your-openai-key-here
|
||||
f.write(env_template)
|
||||
|
||||
# Created .env template
|
||||
console.print(f"[green]✓[/green] Created .env template at {env_path}")
|
||||
logger.info(f"Created .env template at {env_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Failed to create .env template
|
||||
console.print(f"[red]✗[/red] Failed to create .env template: {e}")
|
||||
logger.error(f"Failed to create .env template: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -267,12 +257,12 @@ def create_custom_env_template(variables: Dict[str, str], target_path: Path,
|
||||
f.write(content)
|
||||
|
||||
# Created custom .env template
|
||||
console.print(f"[green]✓[/green] Created custom .env template at {target_path}")
|
||||
logger.info(f"Created custom .env template at {target_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Failed to create custom .env template
|
||||
console.print(f"[red]✗[/red] Failed to create custom .env template: {e}")
|
||||
logger.error(f"Failed to create custom .env template: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: keys.py - API Key Management Handler
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: keys.py
|
||||
# Description: API Key Management Handler
|
||||
# Version: 2.0.0
|
||||
# Category: api/handlers
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v2.0.0 (2025-11-16): Extracted from api_connect.py - complete implementation
|
||||
# - v1.0.0 (2025-11-15): Initial handler stub
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Handler layer (standalone functions)
|
||||
# - Uses CLI service for output
|
||||
# - Uses prax logger for system logs
|
||||
# - Under 300 lines
|
||||
# ==============================================
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
API Key Management Handler
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: provider.py - Provider Configuration Handler
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: provider.py
|
||||
# Description: Provider Configuration Handler
|
||||
# Version: 2.0.0
|
||||
# Category: api/handlers/config
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v2.0.0 (2025-11-16): Complete extraction from api_connect.py
|
||||
# - v1.0.0 (2025-11-15): Initial handler stub
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
@@ -36,16 +31,8 @@ from typing import Dict, Any, Optional
|
||||
# Internal handlers
|
||||
from aipass.api.apps.handlers.json.json_handler import load_json, save_json
|
||||
|
||||
# Console for user feedback
|
||||
try:
|
||||
from rich.console import Console
|
||||
console = Console()
|
||||
except ImportError:
|
||||
# Fallback console if rich not available
|
||||
class SimpleConsole:
|
||||
def print(self, *args, **kwargs):
|
||||
print(*args)
|
||||
console = SimpleConsole()
|
||||
# Logging
|
||||
from aipass.prax import logger
|
||||
|
||||
# =============================================
|
||||
# CONSTANTS
|
||||
@@ -223,12 +210,12 @@ def update_provider_config(provider: str, updates: Dict[str, Any]) -> bool:
|
||||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Updated config for provider
|
||||
console.print(f"[green]✓[/green] Provider config updated: {provider}")
|
||||
logger.info(f"Provider config updated: {provider}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Failed to update provider config
|
||||
console.print(f"[red]✗[/red] Failed to update provider config: {e}")
|
||||
logger.error(f"Failed to update provider config: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -345,7 +332,7 @@ def _create_default_config() -> bool:
|
||||
json.dump(default_config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Created default config
|
||||
console.print(f"[green]✓[/green] Created default config: {config_path}")
|
||||
logger.info(f"Created default config: {config_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: json_handler.py - JSON Auto-Creating Handler
|
||||
# Date: 2025-11-21
|
||||
# =================== AIPass ====================
|
||||
# Name: json_handler.py
|
||||
# Description: JSON Auto-Creating Handler
|
||||
# Version: 1.1.0
|
||||
# Category: api/handlers/json
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.1.0 (2025-11-21): Refactored to comply with error handling
|
||||
# - v1.0.0 (2025-11-13): Initial JSON auto-creation system
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Pure functions with proper error raising
|
||||
# - No Prax imports (handler tier 3)
|
||||
# Created: 2025-11-21
|
||||
# Modified: 2025-11-21
|
||||
# =============================================
|
||||
|
||||
import json
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
|
||||
# =============================================
|
||||
# META DATA HEADER
|
||||
# =================== AIPass ====================
|
||||
# Name: caller.py
|
||||
# Date: 2025-11-16
|
||||
# Description: OpenRouter Caller Detection Handler
|
||||
# Version: 1.0.0
|
||||
# Category: api/handlers
|
||||
#
|
||||
# CHANGELOG:
|
||||
# - v1.0.0 (2025-11-16): Initial caller detection handler extracted from archive
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: client.py - OpenRouter Client Handler
|
||||
# Date: 2025-11-15
|
||||
# =================== AIPass ====================
|
||||
# Name: client.py
|
||||
# Description: OpenRouter Client Handler
|
||||
# Version: 3.0.0
|
||||
# Category: api/handlers/openrouter
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v3.0.0 (2026-02-20): Fallback model chain in get_response() + retry logic in make_api_request()
|
||||
# - v2.0.0 (2025-11-16): Complete extraction from archive - client creation, API requests, response handling
|
||||
# - v1.0.0 (2025-11-15): Initial handler stub
|
||||
# Created: 2025-11-15
|
||||
# Modified: 2025-11-15
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
@@ -31,7 +25,7 @@ Configuration:
|
||||
- Connection pooling via client caching
|
||||
|
||||
Standards:
|
||||
- Uses console.print() for user output (NO print())
|
||||
- Uses prax logger for output (NO print() or console.print())
|
||||
- Uses logger.info() for system logging
|
||||
- Integrates with auth/keys, caller detection, usage tracking handlers
|
||||
- Standalone functions (no classes)
|
||||
@@ -46,8 +40,8 @@ from pathlib import Path
|
||||
import time
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
# AIPASS imports
|
||||
from aipass.cli.apps.modules import console
|
||||
# Logging
|
||||
from aipass.prax import logger
|
||||
|
||||
# OpenAI SDK for OpenRouter compatibility
|
||||
try:
|
||||
@@ -104,12 +98,12 @@ def create_client(api_key: str, base_url: str = OPENROUTER_BASE_URL, timeout: in
|
||||
"""
|
||||
if not OPENAI_AVAILABLE:
|
||||
# logger.error("OpenAI SDK not installed - cannot create client")
|
||||
console.print("[red]Error: OpenAI SDK not installed. Run: pip install openai[/red]")
|
||||
logger.error("OpenAI SDK not installed. Run: pip install openai")
|
||||
return None
|
||||
|
||||
if not api_key:
|
||||
# logger.error("Cannot create client - no API key provided")
|
||||
console.print("[red]Error: API key required for client creation[/red]")
|
||||
logger.error("API key required for client creation")
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -126,7 +120,7 @@ def create_client(api_key: str, base_url: str = OPENROUTER_BASE_URL, timeout: in
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Failed to create OpenRouter client: {e}")
|
||||
console.print(f"[red]Error creating OpenRouter client: {e}[/red]")
|
||||
logger.error(f"Error creating OpenRouter client: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -208,16 +202,16 @@ def make_api_request(client: OpenAI, messages: List[Dict], model: str, retries:
|
||||
try:
|
||||
response = client.chat.completions.create(**api_params)
|
||||
if attempt > 0:
|
||||
print(f"[INFO] API request succeeded on retry {attempt} for model {model}")
|
||||
logger.info(f"API request succeeded on retry {attempt} for model {model}")
|
||||
return response
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
if attempt < retries:
|
||||
delay = 1.0 * (attempt + 1) # 1s, 2s, ...
|
||||
print(f"[INFO] API request failed for {model} (attempt {attempt + 1}/{1 + retries}): {e} — retrying in {delay:.0f}s")
|
||||
logger.info(f"API request failed for {model} (attempt {attempt + 1}/{1 + retries}): {e} — retrying in {delay:.0f}s")
|
||||
time.sleep(delay)
|
||||
|
||||
print(f"[INFO] API request failed for {model} after {1 + retries} attempts: {last_error}")
|
||||
logger.error(f"API request failed for {model} after {1 + retries} attempts: {last_error}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -318,15 +312,15 @@ def get_response(prompt: str, caller: Optional[str] = None, model: Optional[str]
|
||||
# Step 2: Require model from caller - no defaults
|
||||
if not model:
|
||||
# logger.error("No model specified - caller must provide model from their branch config")
|
||||
console.print("[red]Error: No model specified.[/red]")
|
||||
console.print("[yellow]Callers must provide their own model via branch config (e.g., flow_json/openrouter_config.json)[/yellow]")
|
||||
logger.error("No model specified.")
|
||||
logger.warning("Callers must provide their own model via branch config (e.g., flow_json/openrouter_config.json)")
|
||||
return None
|
||||
|
||||
# Step 3: Get API key
|
||||
api_key = get_api_key("openrouter")
|
||||
if not api_key:
|
||||
# logger.error("Cannot get response - no API key available")
|
||||
console.print("[red]Error: No OpenRouter API key available[/red]")
|
||||
logger.error("No OpenRouter API key available")
|
||||
return None
|
||||
|
||||
# Step 4: Get or create client
|
||||
@@ -376,7 +370,7 @@ def clear_client_cache() -> None:
|
||||
count = len(_client_cache)
|
||||
_client_cache.clear()
|
||||
# logger.info(f"Cleared {count} cached clients")
|
||||
console.print(f"[green]Cleared {count} cached OpenRouter clients[/green]")
|
||||
logger.info(f"Cleared {count} cached OpenRouter clients")
|
||||
|
||||
|
||||
def get_cache_stats() -> Dict[str, Any]:
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: models.py - OpenRouter Model Management
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: models.py
|
||||
# Description: OpenRouter Model Management
|
||||
# Version: 1.0.0
|
||||
# Category: api/handlers
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-16): Complete handler - model fetching, filtering, pricing
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
@@ -29,8 +25,8 @@ from pathlib import Path
|
||||
# Standard library imports
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# Console import (NO print())
|
||||
from aipass.cli.apps.modules import console
|
||||
# Logging
|
||||
from aipass.prax import logger
|
||||
|
||||
# Third-party imports
|
||||
import requests
|
||||
@@ -67,7 +63,7 @@ def get_available_models(api_key: Optional[str] = None) -> List[Dict]:
|
||||
|
||||
Example:
|
||||
>>> models = get_available_models()
|
||||
>>> console.print(f"Found {len(models)} models")
|
||||
>>> logger.info(f"Found {len(models)} models")
|
||||
"""
|
||||
try:
|
||||
# Get API key if not provided
|
||||
@@ -76,7 +72,7 @@ def get_available_models(api_key: Optional[str] = None) -> List[Dict]:
|
||||
|
||||
if not api_key:
|
||||
# logger.info(f"[{MODULE_NAME}] No API key available for OpenRouter")
|
||||
console.print("[yellow]No OpenRouter API key found[/yellow]")
|
||||
logger.warning("No OpenRouter API key found")
|
||||
return []
|
||||
|
||||
# Fetch models from API
|
||||
@@ -91,7 +87,7 @@ def get_available_models(api_key: Optional[str] = None) -> List[Dict]:
|
||||
|
||||
except Exception as e:
|
||||
# logger.info(f"[{MODULE_NAME}] Failed to get available models: {e}")
|
||||
console.print(f"[red]Error fetching models: {e}[/red]")
|
||||
logger.error(f"Error fetching models: {e}")
|
||||
return []
|
||||
|
||||
|
||||
@@ -111,7 +107,7 @@ def get_free_models(api_key: Optional[str] = None) -> List[Dict]:
|
||||
Example:
|
||||
>>> free_models = get_free_models()
|
||||
>>> for model in free_models:
|
||||
... console.print(f"Free: {model['id']}")
|
||||
... logger.info(f"Free: {model['id']}")
|
||||
"""
|
||||
try:
|
||||
# Get all models first
|
||||
@@ -128,7 +124,7 @@ def get_free_models(api_key: Optional[str] = None) -> List[Dict]:
|
||||
|
||||
except Exception as e:
|
||||
# logger.info(f"[{MODULE_NAME}] Failed to get free models: {e}")
|
||||
console.print(f"[red]Error fetching free models: {e}[/red]")
|
||||
logger.error(f"Error fetching free models: {e}")
|
||||
return []
|
||||
|
||||
|
||||
@@ -166,7 +162,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}")
|
||||
console.print(f"[red]OpenRouter API error: {response.status_code}[/red]")
|
||||
logger.error(f"OpenRouter API error: {response.status_code}")
|
||||
return []
|
||||
|
||||
# Parse JSON response
|
||||
@@ -183,22 +179,22 @@ def fetch_models_from_api(api_key: str) -> List[Dict]:
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
# logger.info(f"[{MODULE_NAME}] API request timeout after {DEFAULT_TIMEOUT}s")
|
||||
console.print(f"[red]Request timeout - OpenRouter API not responding[/red]")
|
||||
logger.error("Request timeout - OpenRouter API not responding")
|
||||
return []
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
# logger.info(f"[{MODULE_NAME}] Network error: {e}")
|
||||
console.print(f"[red]Network error: {e}[/red]")
|
||||
logger.error(f"Network error: {e}")
|
||||
return []
|
||||
|
||||
except ValueError as e:
|
||||
# logger.info(f"[{MODULE_NAME}] JSON parse error: {e}")
|
||||
console.print(f"[red]Invalid JSON response from API[/red]")
|
||||
logger.error("Invalid JSON response from API")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
# logger.info(f"[{MODULE_NAME}] Unexpected error fetching models: {e}")
|
||||
console.print(f"[red]Error: {e}[/red]")
|
||||
logger.error(f"Error: {e}")
|
||||
return []
|
||||
|
||||
|
||||
@@ -291,7 +287,7 @@ def extract_model_metadata(model: Dict) -> Dict:
|
||||
Example:
|
||||
>>> model = get_model_by_id("meta-llama/llama-3.3-70b-instruct:free")
|
||||
>>> meta = extract_model_metadata(model)
|
||||
>>> console.print(f"Context: {meta['context_length']}")
|
||||
>>> logger.info(f"Context: {meta['context_length']}")
|
||||
"""
|
||||
try:
|
||||
pricing = model.get("pricing", {})
|
||||
@@ -341,27 +337,27 @@ def list_model_ids(models: List[Dict]) -> List[str]:
|
||||
|
||||
def display_models(models: List[Dict], show_pricing: bool = True) -> None:
|
||||
"""
|
||||
Display models in formatted output using console
|
||||
Display models in formatted output using logger
|
||||
|
||||
Args:
|
||||
models: List of model dictionaries to display
|
||||
show_pricing: Whether to show pricing information
|
||||
"""
|
||||
if not models:
|
||||
console.print("[yellow]No models to display[/yellow]")
|
||||
logger.info("No models to display")
|
||||
return
|
||||
|
||||
console.print(f"\n[bold]Found {len(models)} models:[/bold]\n")
|
||||
logger.info(f"Found {len(models)} models:")
|
||||
|
||||
for i, model in enumerate(models, 1):
|
||||
model_id = model.get("id", "unknown")
|
||||
name = model.get("name", "")
|
||||
context = model.get("context_length", 0)
|
||||
|
||||
console.print(f"[cyan]{i}. {model_id}[/cyan]")
|
||||
logger.info(f"{i}. {model_id}")
|
||||
if name:
|
||||
console.print(f" Name: {name}")
|
||||
console.print(f" Context: {context:,} tokens")
|
||||
logger.info(f" Name: {name}")
|
||||
logger.info(f" Context: {context:,} tokens")
|
||||
|
||||
if show_pricing:
|
||||
pricing = model.get("pricing", {})
|
||||
@@ -369,11 +365,9 @@ def display_models(models: List[Dict], show_pricing: bool = True) -> None:
|
||||
completion_cost = pricing.get("completion", "0")
|
||||
|
||||
if prompt_cost == "0" and completion_cost == "0":
|
||||
console.print(" [green]FREE[/green]")
|
||||
logger.info(" FREE")
|
||||
else:
|
||||
console.print(f" Prompt: ${prompt_cost} / Completion: ${completion_cost}")
|
||||
|
||||
console.print()
|
||||
logger.info(f" Prompt: ${prompt_cost} / Completion: ${completion_cost}")
|
||||
|
||||
|
||||
def display_free_models_summary(api_key: Optional[str] = None) -> None:
|
||||
@@ -386,20 +380,20 @@ def display_free_models_summary(api_key: Optional[str] = None) -> None:
|
||||
Args:
|
||||
api_key: Optional OpenRouter API key
|
||||
"""
|
||||
console.print("[bold]Searching for FREE models on OpenRouter...[/bold]")
|
||||
console.print("=" * 60)
|
||||
logger.info("Searching for FREE models on OpenRouter...")
|
||||
logger.info("=" * 60)
|
||||
|
||||
free_models = get_free_models(api_key)
|
||||
|
||||
if not free_models:
|
||||
console.print("[yellow]No free models found[/yellow]")
|
||||
console.print("OpenRouter may have changed pricing.")
|
||||
console.print("Alternative: Use very cheap models like openai/gpt-4o-mini")
|
||||
logger.info("No free models found")
|
||||
logger.info("OpenRouter may have changed pricing.")
|
||||
logger.info("Alternative: Use very cheap models like openai/gpt-4o-mini")
|
||||
return
|
||||
|
||||
display_models(free_models, show_pricing=False)
|
||||
|
||||
console.print("\n[bold]RECOMMENDED FREE MODELS TO TRY:[/bold]")
|
||||
console.print("-" * 60)
|
||||
logger.info("RECOMMENDED FREE MODELS TO TRY:")
|
||||
logger.info("-" * 60)
|
||||
for model in free_models[:5]: # Top 5
|
||||
console.print(f" - {model.get('id', '')}")
|
||||
logger.info(f" - {model.get('id', '')}")
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: provision.py - Caller Auto-Provisioning Handler
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: provision.py
|
||||
# Description: Caller Auto-Provisioning Handler
|
||||
# Version: 1.0.0
|
||||
# Category: api/handlers/openrouter
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-16): Initial handler - auto-provision caller configs
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
@@ -21,7 +17,7 @@ Business logic for provisioning OpenRouter API configs:
|
||||
- Ensure caller has complete 3-file JSON structure
|
||||
|
||||
COMPLIANT STANDARDS:
|
||||
- Uses console.print() for output (NO print())
|
||||
- Uses prax logger for output (NO print() or console.print())
|
||||
- Uses prax logger for operations
|
||||
- Standalone functions (no class dependencies)
|
||||
- Imports from caller handler for detection logic
|
||||
@@ -36,7 +32,7 @@ import json
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any, Optional, Tuple
|
||||
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.prax import logger
|
||||
|
||||
from aipass.api.apps.handlers.openrouter.caller import detect_caller_from_stack
|
||||
|
||||
@@ -168,12 +164,12 @@ def provision_json_folder(json_folder: Path) -> bool:
|
||||
|
||||
json_folder.mkdir(parents=True, exist_ok=True)
|
||||
# logger.info(f"Created JSON folder: {json_folder}")
|
||||
console.print(f"[green]Created JSON folder:[/green] {json_folder}")
|
||||
logger.info(f"Created JSON folder: {json_folder}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
# logger.error(f"Failed to create JSON folder {json_folder}: {e}")
|
||||
console.print(f"[red]Error:[/red] Failed to create JSON folder: {e}")
|
||||
logger.error(f"Failed to create JSON folder: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -207,7 +203,7 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
# logger.info(f"Created API config for {caller}: {config_file}")
|
||||
console.print(f"[green]Created config:[/green] {config_file.name}")
|
||||
logger.info(f"Created config: {config_file.name}")
|
||||
|
||||
# Create data file
|
||||
data_file = json_folder / "openrouter_skill_data.json"
|
||||
@@ -215,7 +211,7 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]:
|
||||
|
||||
if write_json(data_file, data):
|
||||
# logger.info(f"Created data file for {caller}: {data_file}")
|
||||
console.print(f"[green]Created data:[/green] {data_file.name}")
|
||||
logger.info(f"Created data: {data_file.name}")
|
||||
|
||||
# Create log file
|
||||
log_file = json_folder / "openrouter_skill_log.json"
|
||||
@@ -223,16 +219,16 @@ def create_caller_config(caller: str, json_folder: Path) -> Dict[str, Any]:
|
||||
|
||||
if write_json(log_file, log_data):
|
||||
# logger.info(f"Created log file for {caller}: {log_file}")
|
||||
console.print(f"[green]Created log:[/green] {log_file.name}")
|
||||
logger.info(f"Created log: {log_file.name}")
|
||||
|
||||
console.print(f"[cyan]Info:[/cyan] Auto-provisioned OpenRouter config for '{caller}'")
|
||||
console.print(f"[yellow]Note:[/yellow] Reload config and retry request")
|
||||
logger.info(f"Auto-provisioned OpenRouter config for '{caller}'")
|
||||
logger.warning("Reload config and retry request")
|
||||
|
||||
return config
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Failed to create config for {caller}: {e}")
|
||||
console.print(f"[red]Error:[/red] Config creation failed: {e}")
|
||||
logger.error(f"Config creation failed: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
@@ -259,7 +255,7 @@ def ensure_caller_config(caller: str | None = None) -> Dict[str, Any]:
|
||||
# logger.info(f"Auto-detected caller: {caller}")
|
||||
else:
|
||||
# logger.warning("Could not detect caller from stack")
|
||||
console.print("[yellow]Warning:[/yellow] Could not detect caller module")
|
||||
logger.warning("Could not detect caller module")
|
||||
return {}
|
||||
|
||||
# Get JSON folder path if not already detected
|
||||
@@ -267,7 +263,7 @@ def ensure_caller_config(caller: str | None = None) -> Dict[str, Any]:
|
||||
_, json_folder = detect_caller_from_stack()
|
||||
if not json_folder:
|
||||
# logger.error(f"Could not determine JSON folder for {caller}")
|
||||
console.print(f"[red]Error:[/red] Could not find JSON folder for '{caller}'")
|
||||
logger.error(f"Could not find JSON folder for '{caller}'")
|
||||
return {}
|
||||
|
||||
# Check if config already exists
|
||||
@@ -280,14 +276,14 @@ def ensure_caller_config(caller: str | None = None) -> Dict[str, Any]:
|
||||
return config
|
||||
else:
|
||||
# logger.warning(f"Config file corrupted for {caller}, regenerating")
|
||||
console.print(f"[yellow]Warning:[/yellow] Corrupted config, regenerating...")
|
||||
logger.warning("Corrupted config, regenerating...")
|
||||
|
||||
# Create new config
|
||||
return create_caller_config(caller, json_folder)
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Failed to ensure config for {caller}: {e}")
|
||||
console.print(f"[red]Error:[/red] Config provisioning failed: {e}")
|
||||
logger.error(f"Config provisioning failed: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: aggregation.py - Usage Aggregation Handler
|
||||
# Date: 2025-11-15
|
||||
# =================== AIPass ====================
|
||||
# Name: aggregation.py
|
||||
# Description: Usage Aggregation Handler
|
||||
# Version: 1.0.0
|
||||
# Category: api/handlers/usage
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-15): Initial handler - usage stats aggregation
|
||||
# - v1.1.0 (2025-11-16): Extracted aggregation logic from archive
|
||||
# Created: 2025-11-15
|
||||
# Modified: 2025-11-15
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: cleanup.py - Usage data retention and cleanup
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: cleanup.py
|
||||
# Description: Usage data retention and cleanup
|
||||
# Version: 0.1.0
|
||||
# Category: api/handlers
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v0.1.0 (2025-11-16): Extracted from api_usage.py
|
||||
# - v1.0.0 (2025-11-15): Initial handler - old data cleanup
|
||||
#
|
||||
# CODE STANDARDS:
|
||||
# - Handler layer (standalone functions)
|
||||
# - Uses CLI service for output
|
||||
# - Under 300 lines
|
||||
# ==============================================
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Usage Data Cleanup Handler
|
||||
@@ -32,8 +22,8 @@ import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
# CLI services
|
||||
from aipass.cli.apps.modules import console, success, warning
|
||||
# Logging
|
||||
from aipass.prax import logger
|
||||
|
||||
|
||||
def _read_json(file_path: Path) -> Optional[Dict]:
|
||||
@@ -99,13 +89,13 @@ def cleanup_old_data(data_file_path: Path, retention_days: int = 30) -> int:
|
||||
|
||||
_write_json(data_file_path, data)
|
||||
# logger.info(f"Cleaned up {len(old_generations)} generation entries")
|
||||
success(f"Cleaned up {len(old_generations)} generation entries older than {retention_days} days")
|
||||
logger.info(f"Cleaned up {len(old_generations)} generation entries older than {retention_days} days")
|
||||
|
||||
return len(old_generations)
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Cleanup failed: {e}")
|
||||
console.print(f"[red]Cleanup failed: {e}[/red]")
|
||||
logger.error(f"Cleanup failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
@@ -162,13 +152,13 @@ def cleanup_daily_totals(data_file_path: Path, retention_days: int = 90) -> int:
|
||||
|
||||
_write_json(data_file_path, data)
|
||||
# logger.info(f"Cleaned up {len(old_dates)} daily total entries")
|
||||
success(f"Cleaned up {len(old_dates)} daily total entries older than {retention_days} days")
|
||||
logger.info(f"Cleaned up {len(old_dates)} daily total entries older than {retention_days} days")
|
||||
|
||||
return len(old_dates)
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Daily totals cleanup failed: {e}")
|
||||
console.print(f"[red]Daily totals cleanup failed: {e}[/red]")
|
||||
logger.error(f"Daily totals cleanup failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
@@ -190,7 +180,7 @@ def auto_cleanup(data_file_path: Path, config: Optional[Dict] = None) -> Dict[st
|
||||
|
||||
except Exception as e:
|
||||
# logger.error(f"Auto cleanup failed: {e}")
|
||||
console.print(f"[red]Auto cleanup failed: {e}[/red]")
|
||||
logger.error(f"Auto cleanup failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: tracking.py - Usage Tracking Handler
|
||||
# Date: 2025-11-16
|
||||
# =================== AIPass ====================
|
||||
# Name: tracking.py
|
||||
# Description: Usage Tracking Handler
|
||||
# Version: 1.0.0
|
||||
# Category: api/handlers/usage
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-16): Extracted tracking logic from archive
|
||||
# Created: 2025-11-16
|
||||
# Modified: 2025-11-16
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: api_key.py - API Key Management Module
|
||||
# Date: 2025-11-15
|
||||
# =================== AIPass ====================
|
||||
# Name: api_key.py
|
||||
# Description: API Key Management Module
|
||||
# Version: 1.0.0
|
||||
# Category: api/modules
|
||||
# CODE STANDARDS: Seed v1.0.0
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-15): Initial module - orchestrates key management
|
||||
# Created: 2025-11-15
|
||||
# Modified: 2025-11-15
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: openrouter_client.py - OpenRouter Client Module
|
||||
# Date: 2025-11-15
|
||||
# =================== AIPass ====================
|
||||
# Name: openrouter_client.py
|
||||
# Description: OpenRouter Client Module
|
||||
# Version: 1.0.0
|
||||
# Category: api/modules
|
||||
# CODE STANDARDS: Seed v1.0.0
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-15): Initial module - orchestrates OpenRouter operations
|
||||
# Created: 2025-11-15
|
||||
# Modified: 2025-11-15
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
@@ -237,7 +232,7 @@ def get_response(prompt: str, caller: str | None = None, model: str | None = Non
|
||||
>>> from aipass.api.apps.modules.openrouter_client import get_response
|
||||
>>> response = get_response("Hello", caller="flow", model="anthropic/claude-3.5-sonnet")
|
||||
>>> if response:
|
||||
... print(response['content'])
|
||||
... console.print(response['content'])
|
||||
"""
|
||||
return client.get_response(prompt, caller, model, **kwargs)
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
|
||||
# ===================AIPASS====================
|
||||
# META DATA HEADER
|
||||
# Name: usage_tracker.py - Usage Tracking Module
|
||||
# Date: 2025-11-15
|
||||
# =================== AIPass ====================
|
||||
# Name: usage_tracker.py
|
||||
# Description: Usage Tracking Module
|
||||
# Version: 1.0.0
|
||||
# Category: api/modules
|
||||
# CODE STANDARDS: Seed v1.0.0
|
||||
#
|
||||
# CHANGELOG (Max 5 entries):
|
||||
# - v1.0.0 (2025-11-15): Initial module - orchestrates usage tracking
|
||||
# Created: 2025-11-15
|
||||
# Modified: 2025-11-15
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user