- Renamed `check_environment` to `check_api_key_configured` for clarity, simplifying the API key validation logic. - Removed the blocking behavior of the API key check during application startup, allowing the app to run while providing a prompt for configuration. - Updated `LocalAgentApp` to accept an `api_configured` parameter, enabling conditional messaging for API key setup. - Enhanced the `SandboxRunner` to support backup management and improved execution result handling with detailed metrics. - Integrated data governance strategies into the `HistoryManager`, ensuring compliance and improved data management. - Added privacy settings and metrics tracking across various components to enhance user experience and application safety.
168 lines
6.0 KiB
Python
168 lines
6.0 KiB
Python
"""
|
|
配置变更度量模块
|
|
跟踪配置保存后的首次调用成功率和重试次数
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any
|
|
from dataclasses import dataclass, asdict
|
|
|
|
|
|
@dataclass
|
|
class ConfigChangeMetric:
|
|
"""配置变更度量记录"""
|
|
timestamp: str
|
|
config_changed: bool # 是否发生配置变更
|
|
first_call_success: Optional[bool] # 首次调用是否成功
|
|
retry_count: int # 重试次数
|
|
error_message: Optional[str] # 错误信息
|
|
connection_test_success: bool # 保存后连通性测试是否成功
|
|
time_to_success_ms: Optional[int] # 从配置变更到首次成功调用的时间(毫秒)
|
|
|
|
|
|
class ConfigMetricsManager:
|
|
"""配置度量管理器"""
|
|
|
|
def __init__(self, metrics_file: Path):
|
|
self.metrics_file = metrics_file
|
|
self.metrics_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# 当前配置变更状态
|
|
self._config_changed = False
|
|
self._config_change_time: Optional[datetime] = None
|
|
self._connection_test_success = False
|
|
self._first_call_recorded = False
|
|
self._retry_count = 0
|
|
|
|
def mark_config_changed(self, connection_test_success: bool) -> None:
|
|
"""标记配置已变更"""
|
|
self._config_changed = True
|
|
self._config_change_time = datetime.now()
|
|
self._connection_test_success = connection_test_success
|
|
self._first_call_recorded = False
|
|
self._retry_count = 0
|
|
|
|
def record_first_call(self, success: bool, error_message: Optional[str] = None) -> None:
|
|
"""记录配置变更后的首次调用"""
|
|
if not self._config_changed or self._first_call_recorded:
|
|
return
|
|
|
|
time_to_success_ms = None
|
|
if self._config_change_time:
|
|
delta = datetime.now() - self._config_change_time
|
|
time_to_success_ms = int(delta.total_seconds() * 1000)
|
|
|
|
metric = ConfigChangeMetric(
|
|
timestamp=datetime.now().isoformat(),
|
|
config_changed=True,
|
|
first_call_success=success,
|
|
retry_count=self._retry_count,
|
|
error_message=error_message,
|
|
connection_test_success=self._connection_test_success,
|
|
time_to_success_ms=time_to_success_ms
|
|
)
|
|
|
|
self._save_metric(metric)
|
|
self._first_call_recorded = True
|
|
|
|
# 如果成功,重置状态
|
|
if success:
|
|
self._config_changed = False
|
|
self._retry_count = 0
|
|
|
|
def increment_retry(self) -> None:
|
|
"""增加重试计数"""
|
|
if self._config_changed:
|
|
self._retry_count += 1
|
|
|
|
def record_retry_success(self, retry_count: int) -> None:
|
|
"""记录重试后成功的请求"""
|
|
# 可以用于统计重试恢复率
|
|
pass
|
|
|
|
def record_retry_failure(self, retry_count: int) -> None:
|
|
"""记录重试后仍失败的请求"""
|
|
# 可以用于统计重试失败率
|
|
pass
|
|
|
|
def _save_metric(self, metric: ConfigChangeMetric) -> None:
|
|
"""保存度量记录"""
|
|
try:
|
|
# 读取现有记录
|
|
metrics = []
|
|
if self.metrics_file.exists():
|
|
with open(self.metrics_file, 'r', encoding='utf-8') as f:
|
|
metrics = json.load(f)
|
|
|
|
# 添加新记录
|
|
metrics.append(asdict(metric))
|
|
|
|
# 只保留最近 100 条记录
|
|
if len(metrics) > 100:
|
|
metrics = metrics[-100:]
|
|
|
|
# 保存
|
|
with open(self.metrics_file, 'w', encoding='utf-8') as f:
|
|
json.dump(metrics, f, ensure_ascii=False, indent=2)
|
|
except Exception as e:
|
|
print(f"保存配置度量失败: {e}")
|
|
|
|
def get_statistics(self) -> Dict[str, Any]:
|
|
"""获取统计信息"""
|
|
try:
|
|
if not self.metrics_file.exists():
|
|
return {
|
|
"total_config_changes": 0,
|
|
"first_call_success_rate": 0.0,
|
|
"avg_retry_count": 0.0,
|
|
"connection_test_success_rate": 0.0
|
|
}
|
|
|
|
with open(self.metrics_file, 'r', encoding='utf-8') as f:
|
|
metrics = json.load(f)
|
|
|
|
if not metrics:
|
|
return {
|
|
"total_config_changes": 0,
|
|
"first_call_success_rate": 0.0,
|
|
"avg_retry_count": 0.0,
|
|
"connection_test_success_rate": 0.0
|
|
}
|
|
|
|
total = len(metrics)
|
|
success_count = sum(1 for m in metrics if m.get('first_call_success'))
|
|
total_retries = sum(m.get('retry_count', 0) for m in metrics)
|
|
connection_test_success = sum(1 for m in metrics if m.get('connection_test_success'))
|
|
|
|
return {
|
|
"total_config_changes": total,
|
|
"first_call_success_rate": success_count / total if total > 0 else 0.0,
|
|
"avg_retry_count": total_retries / total if total > 0 else 0.0,
|
|
"connection_test_success_rate": connection_test_success / total if total > 0 else 0.0,
|
|
"recent_metrics": metrics[-10:] # 最近 10 条记录
|
|
}
|
|
except Exception as e:
|
|
print(f"获取配置度量统计失败: {e}")
|
|
return {
|
|
"total_config_changes": 0,
|
|
"first_call_success_rate": 0.0,
|
|
"avg_retry_count": 0.0,
|
|
"connection_test_success_rate": 0.0
|
|
}
|
|
|
|
|
|
# 全局单例
|
|
_metrics_manager: Optional[ConfigMetricsManager] = None
|
|
|
|
|
|
def get_config_metrics(workspace: Path) -> ConfigMetricsManager:
|
|
"""获取配置度量管理器单例"""
|
|
global _metrics_manager
|
|
if _metrics_manager is None:
|
|
metrics_file = workspace / ".metrics" / "config_metrics.json"
|
|
_metrics_manager = ConfigMetricsManager(metrics_file)
|
|
return _metrics_manager
|
|
|