86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared_part_01 import *
|
|
|
|
def _normalize_provider(raw: Optional[str]) -> str:
|
|
provider = str(raw or "").strip().lower()
|
|
provider = PROVIDER_ALIASES.get(provider, provider)
|
|
if provider not in SUPPORTED_PROVIDERS:
|
|
raise ValueError("provider 仅支持 deepseek/glm/openai/ollama")
|
|
return provider
|
|
|
|
def _mask_secret(secret: str) -> str:
|
|
value = str(secret or "").strip()
|
|
if not value:
|
|
return ""
|
|
if len(value) <= 8:
|
|
return "*" * len(value)
|
|
return f"{value[:4]}...{value[-4:]}"
|
|
|
|
def _safe_json(value: Any) -> dict[str, Any]:
|
|
if isinstance(value, dict):
|
|
try:
|
|
# 返回深拷贝,避免原地修改导致 SQLAlchemy 不感知 JSON 字段变更
|
|
return json.loads(json.dumps(value))
|
|
except Exception:
|
|
return dict(value)
|
|
if isinstance(value, str):
|
|
try:
|
|
parsed = json.loads(value)
|
|
return parsed if isinstance(parsed, dict) else {}
|
|
except Exception:
|
|
return {}
|
|
return {}
|
|
|
|
def _env_bool(name: str, default: bool = False) -> bool:
|
|
raw = os.getenv(name)
|
|
if raw is None:
|
|
return default
|
|
return str(raw).strip().lower() in BOOL_TRUE_VALUES
|
|
|
|
def _parse_csv_items(raw: str) -> tuple[str, ...]:
|
|
rows = [item.strip().lower() for item in str(raw or "").split(",") if item.strip()]
|
|
return tuple(rows)
|
|
|
|
def _cn_now_text() -> str:
|
|
cn_tz = timezone(timedelta(hours=8))
|
|
return datetime.now(cn_tz).strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
def _cn_now_text_ms() -> str:
|
|
cn_tz = timezone(timedelta(hours=8))
|
|
return datetime.now(cn_tz).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
|
|
def _runtime_log(scope: str, message: str) -> None:
|
|
print(f"[{_cn_now_text()}][{scope}] {message}")
|
|
|
|
def _looks_like_openclaw_timeout_message(text: str) -> bool:
|
|
lowered = str(text or "").strip().lower()
|
|
if not lowered:
|
|
return False
|
|
timeout_signals = (
|
|
"request timed out before a response was generated",
|
|
"agents.defaults.timeoutseconds",
|
|
"timeoutseconds",
|
|
"before a response was generated",
|
|
)
|
|
return any(signal in lowered for signal in timeout_signals)
|
|
|
|
@dataclass
|
|
class ChatCompletionResult:
|
|
text: str
|
|
raw_response: Any = None
|
|
|
|
@dataclass
|
|
class StructuredExtractionResult:
|
|
payload: Optional[dict[str, Any]]
|
|
source_field: str
|
|
provider: str
|
|
model: str
|
|
error_code: Optional[str] = None
|
|
raw_text: str = ""
|
|
validation: str = "invalid"
|
|
from_cache: bool = False
|
|
|
|
|
|
__all__ = [name for name in globals() if not name.startswith("__")]
|