Some checks failed
CD Pipeline / deploy (push) Has been cancelled
- Add ElephantService, AutonomousEngine, Orchestrator, DecisionRouter (EA 4-file stack) - Fix 10 bugs: URL typo, SQL schema mismatches (price_records JOIN), enum mapping, metadata_json, NemoTron PriceThreat dispatch, async/await mismatch, broken imports - Wire ADR-012 Agent Action Ladder: EventRouter L2 → EA first + AIOrch fallback; all decisions dual-write DB + triaged_alert Telegram; momo: callback prefix - Wire ADR-013 AutoHeal: resource_optimization trigger → AutoHealService - Add W3 guards: connection cache 300s TTL, $5/hr cost hard limit - Add W4 persistence: routing decisions + agent performance snapshots → ai_insights - Add Migration 015: confidence + created_by columns on ai_insights - Fix run_scheduler.py broken imports (DecisionTracker service didn't exist) - Fix verify_elephant_integration.py: check_status() → check_connection() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
163 lines
5.4 KiB
Python
163 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Elephant Alpha AI 服務模組
|
||
負責與 OpenRouter / Elephant Alpha API 互動,提供高效率、長上下文的 Worker AI 功能
|
||
"""
|
||
|
||
import os
|
||
import time
|
||
import json
|
||
import logging
|
||
import requests
|
||
from typing import Optional, Dict, Any, List
|
||
from dataclasses import dataclass
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Elephant Alpha 設定
|
||
OPENROUTER_API_KEY = os.getenv('OPENROUTER_API_KEY', '')
|
||
ELEPHANT_ALPHA_URL = "https://openrouter.ai/api/v1/chat/completions"
|
||
DEFAULT_ELEPHANT_MODEL = "openrouter/elephant-alpha"
|
||
ELEPHANT_TIMEOUT = int(os.getenv('ELEPHANT_TIMEOUT', '120')) # 預設 2 分鐘
|
||
|
||
# Elephant Alpha 定價 (USD per 1M tokens) - 預估效能型定價
|
||
ELEPHANT_PRICING = {
|
||
'openrouter/elephant-alpha': {'input': 0.10, 'output': 0.40},
|
||
}
|
||
|
||
@dataclass
|
||
class ElephantResponse:
|
||
"""Elephant Alpha 回應結構"""
|
||
success: bool
|
||
content: str
|
||
model: str
|
||
error: Optional[str] = None
|
||
total_duration: Optional[float] = None
|
||
input_tokens: int = 0
|
||
output_tokens: int = 0
|
||
total_tokens: int = 0
|
||
input_cost: float = 0.0
|
||
output_cost: float = 0.0
|
||
total_cost: float = 0.0
|
||
|
||
class ElephantService:
|
||
"""Elephant Alpha AI 服務 - 100B 效率型 Worker"""
|
||
|
||
def __init__(self, api_key: str = None, model: str = None):
|
||
"""
|
||
初始化 Elephant 服務
|
||
"""
|
||
self.api_key = api_key or OPENROUTER_API_KEY
|
||
self.model = model or DEFAULT_ELEPHANT_MODEL
|
||
|
||
# W3-A: 護欄 2 — 斷線降級 cache (300s TTL,不每次 ping OpenRouter)
|
||
_connection_cache: Dict[str, Any] = {"ok": False, "checked_at": 0.0}
|
||
|
||
def check_connection(self, cache_seconds: int = 300) -> bool:
|
||
"""
|
||
檢查 API 是否可用(結果快取 300s)。
|
||
cache_seconds=300 與 Anthropic prompt cache TTL 對齊,避免每分鐘 EA loop 都打 API。
|
||
"""
|
||
if not self.api_key:
|
||
return False
|
||
|
||
now = time.time()
|
||
cache = ElephantService._connection_cache
|
||
if cache["checked_at"] and (now - cache["checked_at"]) < cache_seconds:
|
||
return cache["ok"]
|
||
|
||
try:
|
||
response = self.generate("hi", timeout=10)
|
||
result = response.success
|
||
except Exception:
|
||
result = False
|
||
|
||
ElephantService._connection_cache = {"ok": result, "checked_at": now}
|
||
return result
|
||
|
||
@staticmethod
|
||
def calculate_cost(model: str, input_tokens: int, output_tokens: int) -> Dict[str, float]:
|
||
"""計算費用"""
|
||
pricing = ELEPHANT_PRICING.get(model, ELEPHANT_PRICING['openrouter/elephant-alpha'])
|
||
input_cost = (input_tokens / 1_000_000) * pricing['input']
|
||
output_cost = (output_tokens / 1_000_000) * pricing['output']
|
||
return {
|
||
'input_cost': round(input_cost, 6),
|
||
'output_cost': round(output_cost, 6),
|
||
'total_cost': round(input_cost + output_cost, 6)
|
||
}
|
||
|
||
def generate(self, prompt: str, model: str = None,
|
||
system_prompt: str = None, temperature: float = 0.3,
|
||
json_mode: bool = False, timeout: int = None) -> ElephantResponse:
|
||
"""
|
||
生成文字(主介面)
|
||
"""
|
||
model_name = model or self.model
|
||
request_timeout = timeout or ELEPHANT_TIMEOUT
|
||
|
||
if not self.api_key:
|
||
return ElephantResponse(success=False, content='', model=model_name, error="API Key 未設定")
|
||
|
||
headers = {
|
||
"Authorization": f"Bearer {self.api_key}",
|
||
"Content-Type": "application/json"
|
||
}
|
||
|
||
messages = []
|
||
if system_prompt:
|
||
messages.append({"role": "system", "content": system_prompt})
|
||
messages.append({"role": "user", "content": prompt})
|
||
|
||
payload = {
|
||
"model": model_name,
|
||
"messages": messages,
|
||
"temperature": temperature,
|
||
"max_tokens": 8000
|
||
}
|
||
|
||
if json_mode:
|
||
payload["response_format"] = {"type": "json_object"}
|
||
|
||
try:
|
||
start_time = time.time()
|
||
response = requests.post(
|
||
ELEPHANT_ALPHA_URL,
|
||
json=payload,
|
||
headers=headers,
|
||
timeout=request_timeout
|
||
)
|
||
response.raise_for_status()
|
||
end_time = time.time()
|
||
|
||
data = response.json()
|
||
content = data["choices"][0]["message"]["content"]
|
||
|
||
# Token 用量
|
||
usage = data.get("usage", {})
|
||
input_tokens = usage.get("prompt_tokens", 0)
|
||
output_tokens = usage.get("completion_tokens", 0)
|
||
|
||
costs = self.calculate_cost(model_name, input_tokens, output_tokens)
|
||
|
||
return ElephantResponse(
|
||
success=True,
|
||
content=content,
|
||
model=model_name,
|
||
total_duration=end_time - start_time,
|
||
input_tokens=input_tokens,
|
||
output_tokens=output_tokens,
|
||
total_tokens=input_tokens + output_tokens,
|
||
input_cost=costs['input_cost'],
|
||
output_cost=costs['output_cost'],
|
||
total_cost=costs['total_cost']
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"[Elephant] 生成失敗: {e}")
|
||
return ElephantResponse(success=False, content='', model=model_name, error=str(e))
|
||
|
||
# 單例實例
|
||
elephant_service = ElephantService()
|