agent-Specialization/utils/api_client/logging_mixin.py
JOJO 8e1a339102 refactor: split api_client, tool_result_formatter, tools_definition into sub-packages
- utils/api_client.py -> utils/api_client/ (mixin-based)
- utils/tool_result_formatter.py -> utils/tool_result_formatter/ (by tool category)
- core/main_terminal_parts/tools_definition.py -> core/main_terminal_parts/tools_definition/ (by tool group)
- Update AGENTS.md, CLAUDE.md, and split memory index
- Keep original files as compatibility re-exports
- Includes TerminalPanel display adjustment and ToolAction.vue diff line fix
2026-06-20 21:51:45 +08:00

96 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ========== api_client.py ==========
# utils/api_client.py - OpenAI-compatible API 客户端支持Web模式
import httpx
import json
import asyncio
import base64
import mimetypes
import os
from typing import List, Dict, Optional, AsyncGenerator, Any
from pathlib import Path
from datetime import datetime
from pathlib import Path
from typing import Tuple
try:
from config import (
OUTPUT_FORMATS,
DEFAULT_RESPONSE_MAX_TOKENS,
LOGS_DIR,
)
except ImportError:
import sys
from pathlib import Path
project_root = Path(__file__).resolve().parents[1]
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from config import (
OUTPUT_FORMATS,
DEFAULT_RESPONSE_MAX_TOKENS,
LOGS_DIR,
)
from utils.log_rotation import append_line, prune_dir
from utils.api_client.utils import _api_dump_enabled
class DeepSeekClientLoggingMixin:
def _debug_log(self, payload: Dict[str, Any]) -> None:
if not _api_dump_enabled():
return
try:
entry = {
"ts": datetime.now().isoformat(),
**payload
}
append_line(self.debug_log_path, json.dumps(entry, ensure_ascii=False))
except Exception:
pass
def _dump_request_payload(self, payload: Dict, api_config: Dict, headers: Dict) -> Optional[Path]:
"""
将本次请求的payload、headers、配置落盘便于排查400等错误。
返回写入的文件路径;落盘开关关闭(默认)时返回 None。
"""
if not _api_dump_enabled():
return None
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
filename = f"req_{timestamp}.json"
path = self.request_dump_dir / filename
try:
self.request_dump_dir.mkdir(parents=True, exist_ok=True)
headers_sanitized = {}
for k, v in headers.items():
headers_sanitized[k] = "***" if k.lower() == "authorization" else v
data = {
"timestamp": datetime.now().isoformat(),
"api_config": {k: api_config.get(k) for k in ["base_url", "model_id"]},
"headers": headers_sanitized,
"payload": payload
}
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
# 按份保留:只留最近 N 个请求落盘文件
prune_dir(self.request_dump_dir, pattern="req_*.json")
except Exception as exc:
self._print(f"{OUTPUT_FORMATS['warning']} 请求体落盘失败: {exc}")
return path
def _mark_request_error(self, dump_path: Path, status_code: int = None, error_text: str = None):
"""
在已有请求文件中追加错误标记,便于快速定位。
"""
if not dump_path or not dump_path.exists():
return
try:
data = json.loads(dump_path.read_text(encoding="utf-8"))
data["error"] = {
"status_code": status_code,
"message": error_text,
"marked_at": datetime.now().isoformat()
}
dump_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
except Exception as exc:
self._print(f"{OUTPUT_FORMATS['warning']} 标记请求错误失败: {exc}")