TalkingHeadBenchEnv / server /llm_adapter.py
elix3r's picture
Upload folder using huggingface_hub
d7237d8 verified
Raw
History Blame Contribute Delete
37.6 kB
"""Plug-and-play LLM adapter for TalkingHeadBench ingestion bundles."""
from __future__ import annotations
import ipaddress
import json
import os
from typing import Any, Literal
from urllib.parse import urlparse
from openai import APIConnectionError, APIStatusError, OpenAI
Provider = Literal["openai", "anthropic", "huggingface", "local"]
TaskTier = Literal[
"image_audit",
"clip_audit",
"weight_audit",
"easy",
"medium",
"hard",
]
_DRIFT_WARNING_THRESHOLD = 0.22
_DRIFT_HIGH_RISK_THRESHOLD = 0.35
_BLUR_WARNING_THRESHOLD = 0.30
_BLUR_HIGH_RISK_THRESHOLD = 0.20
_LIPSYNC_WARNING_THRESHOLD = 0.40
_LIPSYNC_HIGH_RISK_THRESHOLD = 0.30
_SYSTEM_PROMPT = (
"You are a TalkingHeadBench diagnostic assistant specializing in talking-head LoRA pipelines.\n"
"You receive only pre-extracted, deterministic signals and never raw generated outputs.\n"
"Rules:\n"
"1. Every claim must cite a specific digest field name and measured value.\n"
"2. Parameter recommendations must be directional only (increase/decrease/enable/disable/reconsider) and never include target numbers.\n"
"3. If fallback extractor data is present, treat fallback clip lip-sync and phoneme signals as unreliable.\n"
"4. Do not invent any signal that is not present in the digest.\n"
"5. Follow the required section structure from the user prompt exactly."
)
class LLMAdapterError(Exception):
"""Structured error surfaced by the one-shot analysis adapter."""
def __init__(
self,
*,
code: str,
message: str,
status_code: int,
retryable: bool = False,
) -> None:
super().__init__(message)
self.code = code
self.message = message
self.status_code = status_code
self.retryable = retryable
def analyze_ingested_bundle(
bundle: dict[str, Any],
*,
model_id: str | None,
api_key: str | None,
provider: str | None,
base_url: str | None,
max_tokens: int,
temperature: float,
timeout_s: float,
task_tier: TaskTier = "weight_audit",
) -> dict[str, Any]:
"""Generate a one-shot natural-language report from an ingested signal bundle."""
if not isinstance(bundle, dict):
raise LLMAdapterError(
code="invalid_ingestion_bundle",
message="The ingested bundle payload is invalid.",
status_code=400,
retryable=False,
)
resolved_provider = _resolve_provider(
provider=provider,
model_id=model_id,
api_key=api_key,
base_url=base_url,
)
resolved_model = _resolve_model_id(resolved_provider, model_id)
resolved_base_url = _resolve_custom_base_url(
provider=resolved_provider,
base_url=base_url,
)
signal_digest = _build_signal_digest(bundle)
prompt = _build_prompt(signal_digest, task_tier=task_tier)
if resolved_provider == "openai":
report = _call_openai(
model_id=resolved_model,
api_key=api_key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
base_url=resolved_base_url,
)
elif resolved_provider == "anthropic":
report = _call_anthropic(
model_id=resolved_model,
api_key=api_key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
base_url=resolved_base_url,
)
elif resolved_provider == "huggingface":
report = _call_huggingface(
model_id=resolved_model,
api_key=api_key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
base_url=resolved_base_url,
)
else:
report = _call_local(
model_id=resolved_model,
api_key=api_key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
base_url=resolved_base_url,
)
text = (report or "").strip()
if not text:
raise LLMAdapterError(
code="empty_model_response",
message="The model provider returned an empty analysis response.",
status_code=502,
retryable=True,
)
return {
"provider": resolved_provider,
"model_id": resolved_model,
"report": text,
"signal_digest": signal_digest,
}
def _resolve_provider(
*,
provider: str | None,
model_id: str | None,
api_key: str | None,
base_url: str | None,
) -> Provider:
requested = (provider or "auto").strip().lower()
if requested == "hf":
requested = "huggingface"
if requested not in {"auto", "openai", "anthropic", "huggingface", "local"}:
raise LLMAdapterError(
code="unsupported_provider",
message=(
"provider must be one of: auto, openai, anthropic, huggingface, local"
),
status_code=400,
retryable=False,
)
if requested != "auto":
return requested # type: ignore[return-value]
key = (api_key or "").strip()
model = (model_id or os.getenv("MODEL_NAME") or "").strip().lower()
resolved_base = (base_url or os.getenv("API_BASE_URL") or "").strip().lower()
if key.startswith("sk-ant-") or model.startswith("claude"):
return "anthropic"
if key.startswith("hf_"):
return "huggingface"
if key.startswith("sk-") or model.startswith(("gpt-", "o1", "o3", "o4")):
return "openai"
if "/" in model:
return "huggingface"
if "router.huggingface.co" in resolved_base or "api-inference.huggingface.co" in resolved_base:
return "huggingface"
if "api.openai.com" in resolved_base:
return "openai"
if "api.anthropic.com" in resolved_base:
return "anthropic"
if resolved_base and not any(
marker in resolved_base
for marker in ["localhost", "127.0.0.1", "0.0.0.0", "11434"]
):
# Any configured remote OpenAI-compatible base should avoid local fallback.
return "openai"
return "local"
def _resolve_model_id(provider: Provider, model_id: str | None) -> str:
cleaned = (model_id or "").strip()
if cleaned:
return cleaned
global_model = os.getenv("MODEL_NAME", "").strip()
if global_model:
return global_model
if provider == "openai":
return os.getenv("THB_OPENAI_MODEL", "gpt-4o-mini")
if provider == "anthropic":
return os.getenv("THB_ANTHROPIC_MODEL", "claude-3-5-haiku-latest")
if provider == "huggingface":
env_model = os.getenv("THB_HF_MODEL", "").strip()
if env_model:
return env_model
# Default to a well-supported chat model via the HF router.
return "Qwen/Qwen2.5-72B-Instruct"
return os.getenv("THB_LOCAL_MODEL", "llama3.1:8b-instruct-q4_K_M")
def _env_truthy(name: str, *, default: bool) -> bool:
raw = os.getenv(name)
if raw is None:
return default
return raw.strip().lower() in {"1", "true", "yes", "on"}
def _allowed_base_url_prefixes() -> list[str]:
raw = os.getenv("THB_ALLOWED_BASE_URL_PREFIXES", "")
prefixes = [item.strip().rstrip("/") for item in raw.split(",") if item.strip()]
return prefixes
def _resolve_custom_base_url(*, provider: Provider, base_url: str | None) -> str | None:
custom = (base_url or "").strip()
if not custom:
return None
configured_api_base = (os.getenv("API_BASE_URL") or "").strip()
if configured_api_base and _normalize_base_url(custom) == _normalize_base_url(configured_api_base):
return custom
if not _env_truthy("THB_ALLOW_CUSTOM_BASE_URLS", default=False):
raise LLMAdapterError(
code="custom_base_url_disabled",
message="Custom base_url is disabled for this deployment.",
status_code=400,
retryable=False,
)
_validate_custom_base_url(custom, provider=provider)
allowed_prefixes = _allowed_base_url_prefixes()
if allowed_prefixes:
normalized = custom.rstrip("/")
if not any(normalized.startswith(prefix) for prefix in allowed_prefixes):
raise LLMAdapterError(
code="base_url_not_allowed",
message="base_url is not in THB_ALLOWED_BASE_URL_PREFIXES.",
status_code=400,
retryable=False,
)
return custom
def _normalize_base_url(url: str) -> str:
return url.strip().rstrip("/")
def _validate_custom_base_url(base_url: str, *, provider: Provider) -> None:
parsed = urlparse(base_url)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise LLMAdapterError(
code="invalid_base_url",
message="base_url must be a valid http(s) URL.",
status_code=400,
retryable=False,
)
if parsed.username or parsed.password:
raise LLMAdapterError(
code="invalid_base_url",
message="base_url must not contain embedded credentials.",
status_code=400,
retryable=False,
)
host = (parsed.hostname or "").strip().lower()
if not host:
raise LLMAdapterError(
code="invalid_base_url",
message="base_url must include a hostname.",
status_code=400,
retryable=False,
)
if host in {"localhost"} or host.endswith(".local"):
raise LLMAdapterError(
code="unsafe_base_url",
message=(
f"base_url host is not allowed for public deployment (provider={provider})."
),
status_code=400,
retryable=False,
)
try:
ip = ipaddress.ip_address(host)
except ValueError:
return
if (
ip.is_private
or ip.is_loopback
or ip.is_link_local
or ip.is_multicast
or ip.is_reserved
):
raise LLMAdapterError(
code="unsafe_base_url",
message=(
f"base_url host is not allowed for public deployment (provider={provider})."
),
status_code=400,
retryable=False,
)
def _require_api_key(provider: str, api_key: str | None) -> str:
key = (api_key or "").strip()
if key:
return key
raise LLMAdapterError(
code="missing_api_key",
message=f"Provider '{provider}' requires api_key.",
status_code=400,
retryable=False,
)
def _build_signal_digest(bundle: dict[str, Any]) -> dict[str, Any]:
image_obs = _as_dict(bundle.get("image_observation"))
clip_obs = _as_list_of_dicts(bundle.get("clip_signal_observations"))
weight_obs = _as_dict(bundle.get("weight_observation"))
metadata = _as_dict(bundle.get("ingestion_metadata"))
extractor_metadata = _as_dict(metadata.get("extractor_metadata"))
fallback_clip_ids = {
str(item).strip()
for item in _as_list(extractor_metadata.get("fallback_clip_ids"))
if str(item).strip()
}
drifts: list[float] = []
blurs: list[float] = []
lips: list[float] = []
reliable_lips: list[float] = []
fallback_clip_count = 0
high_risk_clip_ids: list[str] = []
high_risk_clips: list[dict[str, Any]] = []
quality_distribution: dict[str, int] = {
"good": 0,
"acceptable": 0,
"marginal": 0,
"poor": 0,
}
per_clip_breakdown: list[dict[str, Any]] = []
per_clip_signals: list[dict[str, Any]] = []
max_per_clip_signals = _max_clip_signals_limit()
for index, item in enumerate(clip_obs):
clip_id = str(item.get("clip_id", "unknown"))
drift = _as_float(item.get("identity_cosine_drift"), default=0.0)
blur = _as_float(item.get("blur_score"), default=1.0)
lip_sync = _as_float(item.get("lip_sync_confidence"), default=1.0)
fallback_used = _as_bool(item.get("fallback_extractor_used")) or clip_id in fallback_clip_ids
lip_sync_reliable = not fallback_used
if fallback_used:
fallback_clip_count += 1
drifts.append(drift)
blurs.append(blur)
lips.append(lip_sync)
if lip_sync_reliable:
reliable_lips.append(lip_sync)
high_risk_reasons, warning_reasons = _clip_risk_reasons(
drift=drift,
blur=blur,
lip_sync=lip_sync,
lip_sync_reliable=lip_sync_reliable,
fallback_used=fallback_used,
)
high_risk_reason_count = len(high_risk_reasons)
is_high_risk = high_risk_reason_count >= 2
has_warning = bool(warning_reasons) or high_risk_reason_count == 1
risk_level = "high_risk" if is_high_risk else ("warning" if has_warning else "clean")
quality_tier = _clip_quality_tier(
drift=drift,
blur=blur,
lip_sync=lip_sync,
lip_sync_reliable=lip_sync_reliable,
fallback_used=fallback_used,
high_risk_reason_count=high_risk_reason_count,
)
quality_distribution[quality_tier] += 1
if is_high_risk:
high_risk_clip_ids.append(clip_id)
high_risk_clips.append(
{
"clip_id": clip_id,
"reasons": high_risk_reasons,
"risk_level": risk_level,
"quality_tier": quality_tier,
"lip_sync_reliable": lip_sync_reliable,
}
)
if index < max_per_clip_signals:
per_clip_signals.append(
{
"clip_id": clip_id,
"identity_cosine_drift": drift,
"drift_severity": _drift_severity(drift),
"blur_score": blur,
"lip_sync_confidence": lip_sync,
"phoneme_coverage_new": _as_int(item.get("phoneme_coverage_new")),
"blink_count": _as_int(item.get("blink_count")),
"occlusion_frames": _as_int(item.get("occlusion_frames")),
"fallback_extractor_used": fallback_used,
"provisional_due_to_fallback": fallback_used,
"lip_sync_reliable": lip_sync_reliable,
"is_high_risk": is_high_risk,
"risk_level": risk_level,
"high_risk_reasons": high_risk_reasons,
"warning_reasons": warning_reasons,
"quality_tier": quality_tier,
}
)
per_clip_breakdown.append(
{
"clip_id": clip_id,
"identity_cosine_drift": round(drift, 4),
"blur_score": round(blur, 4),
"lip_sync_confidence": round(lip_sync, 4),
"landmark_stability_score": _as_float(item.get("landmark_stability_score")),
"phoneme_coverage_new": _as_float(item.get("phoneme_coverage_new")),
"exposure_score": _as_float(item.get("exposure_score")),
"occlusion_frames": _as_int(item.get("occlusion_frames")),
"fallback_extractor_used": fallback_used,
"risk_flags": (
(["high_drift"] if drift >= _DRIFT_WARNING_THRESHOLD else [])
+ (["low_blur"] if blur <= _BLUR_WARNING_THRESHOLD else [])
+ (["poor_lip_sync"] if lip_sync <= _LIPSYNC_WARNING_THRESHOLD else [])
),
}
)
layer_entropy = _as_dict(weight_obs.get("canonical_entropy_per_layer"))
rank_util = _as_dict(weight_obs.get("layer_rank_utilization"))
layer_sparsity = _as_dict(weight_obs.get("layer_sparsity"))
gradient_noise = _as_float(weight_obs.get("gradient_noise_estimate"))
high_entropy_positions = _as_list(weight_obs.get("high_entropy_token_positions"))
token_map = weight_obs.get("token_position_to_phoneme")
configured_fallback_count = _as_int(extractor_metadata.get("clip_extractor_fallback_count"))
if configured_fallback_count > fallback_clip_count:
fallback_clip_count = configured_fallback_count
rank_values = [_as_float(value) for value in rank_util.values()]
mean_rank_utilization = (
round(sum(rank_values) / len(rank_values), 4)
if rank_values
else None
)
min_rank_layer = (
min(rank_util, key=lambda key: _as_float(rank_util[key]))
if rank_util
else None
)
min_rank_value = _dict_min(rank_util) if rank_util else None
worst_sparsity_layer = (
max(layer_sparsity, key=lambda key: _as_float(layer_sparsity[key]))
if layer_sparsity
else None
)
worst_sparsity_value = (
_as_float(layer_sparsity.get(worst_sparsity_layer))
if worst_sparsity_layer is not None
else None
)
overfit_sig = _as_float(weight_obs.get("overfitting_signature"))
mean_rank_for_assessment = (
sum(_as_float(v) for v in rank_util.values()) / max(len(rank_util), 1)
if rank_util
else 0.5
)
if overfit_sig >= 0.6:
training_quality_assessment = "overfit"
elif gradient_noise >= 0.5:
training_quality_assessment = "unstable"
elif mean_rank_for_assessment <= 0.3:
training_quality_assessment = "underfit"
else:
training_quality_assessment = "healthy"
digest: dict[str, Any] = {
"case_id": bundle.get("case_id"),
"prompt": str(bundle.get("prompt", "")),
"param_config": _as_dict(bundle.get("param_config")),
"ingestion_metadata": {
"created_at_unix": metadata.get("created_at_unix"),
"clip_extractor_fallback_count": fallback_clip_count,
"fallback_clip_ids": sorted(fallback_clip_ids),
},
}
if image_obs:
digest["image_summary"] = {
"face_occupancy_ratio": _as_float(image_obs.get("face_occupancy_ratio")),
"estimated_sharpness": _as_float(image_obs.get("estimated_sharpness")),
"estimated_yaw_degrees": _as_float(image_obs.get("estimated_yaw_degrees")),
"estimated_pitch_degrees": _as_float(image_obs.get("estimated_pitch_degrees")),
"lighting_uniformity_score": _as_float(image_obs.get("lighting_uniformity_score")),
"background_complexity_score": _as_float(
image_obs.get("background_complexity_score")
),
"occlusion_detected": _as_bool(image_obs.get("occlusion_detected")),
"prompt_token_count": _as_int(image_obs.get("prompt_token_count")),
"prompt_semantic_density": _as_float(image_obs.get("prompt_semantic_density")),
"conflicting_descriptors": _as_list(image_obs.get("conflicting_descriptors")),
"identity_anchoring_strength": _as_float(
image_obs.get("identity_anchoring_strength")
),
"face_detection_measured": _as_bool(
extractor_metadata.get("face_detection_measured"),
default=True,
),
}
if clip_obs:
fallback_clips_note = (
"Clips extracted via fallback extractor use synthetic lip_sync_confidence=0.35 "
"and empty phoneme_sequence; treat lip-sync and phoneme evidence as unreliable."
if fallback_clip_count > 0
else None
)
digest["clip_summary"] = {
"clip_count": len(clip_obs),
"fallback_clip_count": fallback_clip_count,
"non_fallback_clip_count": max(0, len(clip_obs) - fallback_clip_count),
"all_clips_fallback": fallback_clip_count == len(clip_obs),
"parameter_tuning_reliability": (
"low" if fallback_clip_count == len(clip_obs) else "normal"
),
"high_risk_clip_ids": high_risk_clip_ids,
"high_risk_clips": high_risk_clips,
"mean_identity_drift": _safe_mean(drifts),
"mean_blur_score": _safe_mean(blurs),
"mean_lip_sync_confidence": _safe_mean(lips),
"mean_lip_sync_confidence_reliable_only": _safe_mean(reliable_lips),
"quality_distribution": quality_distribution,
"per_clip_signals": per_clip_signals,
"per_clip_breakdown": per_clip_breakdown,
"per_clip_signals_truncated": len(clip_obs) > max_per_clip_signals,
"fallback_clips_note": fallback_clips_note,
}
if weight_obs:
digest["weight_summary"] = {
"available": True,
"weight_file_id": str(weight_obs.get("weight_file_id", "")),
"lora_rank": _as_int(weight_obs.get("lora_rank")),
"target_module_count": len(_as_list(weight_obs.get("target_modules"))),
"max_canonical_entropy": _dict_max(layer_entropy),
"min_rank_utilization": _dict_min(rank_util),
"high_entropy_token_positions": high_entropy_positions[:24],
"suspected_anomalous_phonemes": _phonemes_from_positions(
high_entropy_positions,
token_map,
),
"overfitting_signature": overfit_sig,
"layer_rank_utilization_summary": {
"mean": mean_rank_utilization,
"min_layer": min_rank_layer,
"min_value": min_rank_value,
},
"worst_sparsity_layer": worst_sparsity_layer,
"worst_sparsity_value": worst_sparsity_value,
"gradient_noise_estimate": gradient_noise,
"training_quality_assessment": training_quality_assessment,
}
return digest
def _normalize_task_tier(task_tier: TaskTier) -> Literal["image_audit", "clip_audit", "weight_audit"]:
mapped = {
"easy": "image_audit",
"medium": "clip_audit",
"hard": "weight_audit",
}
return mapped.get(task_tier, task_tier) # type: ignore[return-value]
def _build_prompt(
signal_digest: dict[str, Any],
*,
task_tier: TaskTier = "weight_audit",
) -> str:
_ = _normalize_task_tier(task_tier)
fallback_note = (
_as_dict(signal_digest.get("clip_summary")).get("fallback_clips_note")
if isinstance(signal_digest.get("clip_summary"), dict)
else None
)
fallback_warning = (
f"\n\nWARNING - FALLBACK EXTRACTOR ACTIVE: {fallback_note}"
if isinstance(fallback_note, str) and fallback_note.strip()
else ""
)
return (
"Analyze the TalkingHeadBench signal digest below and produce a structured report.\n\n"
"## Signal Thresholds (use these to classify findings)\n"
"Identity drift severity: <0.05=none, <0.12=minor, <0.22=moderate, >=0.22=severe\n"
"Lip sync quality: >=0.75=good, >=0.50=acceptable, >=0.20=poor, <0.20=absent\n"
"Blur score: >=0.60=sharp, >=0.30=acceptable, <0.30=blurry\n"
"Overfitting signature: <0.30=low, 0.30-0.59=medium, 0.60-0.79=high, >=0.80=critical\n"
"Rank utilization mean: >=0.65=efficient, 0.30-0.64=wasteful, <0.30=collapsed\n"
"Face occupancy ratio: >=0.20=good, 0.10-0.19=marginal, <0.10=face too small\n\n"
"## Required Report Sections\n"
"Write exactly these sections in order. Each section must cite specific digest values.\n\n"
"### Overall Readiness\n"
"1-2 sentences. State readiness level (ready / marginal / not ready) and the single most critical blocker with its measured value.\n\n"
"### Critical Risks\n"
"Bullet list. For each risk: name the affected signal, cite exact value, and classify severity with thresholds above. Minimum 1 bullet, maximum 5.\n\n"
"### Parameter Fixes\n"
"Directional only - no specific numbers. For each anomalous param in param_config, state direction to adjust and why, citing the signal that drives it. If param_config is reasonable, state that explicitly.\n\n"
"### Data and Weight Concerns\n"
"Per-clip breakdown: for each entry in per_clip_breakdown, state clip_id and risk_flags (or clean if none). Then summarize weight health using overfitting_signature, layer_rank_utilization_summary.mean, and worst_sparsity_layer.\n\n"
"### Weight Status\n"
"If weight_summary.available is false or absent, state 'Not uploaded - no data'. Otherwise state training quality assessment (overfit/healthy/underfit/unstable) based on training_quality_assessment and supporting fields.\n\n"
"### Top 3 Next Actions\n"
"Numbered list. Each action must be actionable, specific, and evidence-cited.\n\n"
"### Confidence\n"
"State confidence level (high/medium/low) and what limits it (fallback extractor, missing weights, or insufficient clips)."
f"{fallback_warning}"
"\n\n## Signal Digest\n"
f"{json.dumps(signal_digest, indent=2, sort_keys=True)}"
)
def _call_openai(
*,
model_id: str,
api_key: str | None,
prompt: str,
max_tokens: int,
temperature: float,
timeout_s: float,
base_url: str | None,
) -> str:
key = _require_api_key("openai", api_key)
base = _resolve_openai_base_url(base_url)
return _call_chat_completions(
provider="openai",
base_url=base,
model_id=model_id,
api_key=key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
)
def _call_anthropic(
*,
model_id: str,
api_key: str | None,
prompt: str,
max_tokens: int,
temperature: float,
timeout_s: float,
base_url: str | None,
) -> str:
key = _require_api_key("anthropic", api_key)
base = _resolve_anthropic_base_url(base_url)
return _call_chat_completions(
provider="anthropic",
base_url=base,
model_id=model_id,
api_key=key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
)
def _call_huggingface(
*,
model_id: str,
api_key: str | None,
prompt: str,
max_tokens: int,
temperature: float,
timeout_s: float,
base_url: str | None,
) -> str:
key = _require_api_key("huggingface", api_key)
routed_model = model_id
if not base_url and ":" not in model_id.split("/")[-1]:
routed_model = f"{model_id}:fastest"
base = _resolve_huggingface_base_url(base_url)
return _call_chat_completions(
provider="huggingface",
base_url=base,
model_id=routed_model,
api_key=key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
)
def _call_local(
*,
model_id: str,
api_key: str | None,
prompt: str,
max_tokens: int,
temperature: float,
timeout_s: float,
base_url: str | None,
) -> str:
key = (api_key or os.getenv("HF_TOKEN") or "local").strip()
base = _resolve_local_base_url(base_url)
return _call_chat_completions(
provider="local",
base_url=base,
model_id=model_id,
api_key=key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
timeout_s=timeout_s,
)
def _call_chat_completions(
*,
provider: str,
base_url: str,
model_id: str,
api_key: str,
prompt: str,
max_tokens: int,
temperature: float,
timeout_s: float,
) -> str:
client = OpenAI(api_key=api_key, base_url=base_url, timeout=timeout_s)
try:
response = client.chat.completions.create(
model=model_id,
messages=[
{"role": "system", "content": _SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
temperature=temperature,
max_tokens=max_tokens,
)
except APIStatusError as exc:
status_code = int(exc.status_code or 502)
message = _format_http_error(provider, status_code)
raise LLMAdapterError(
code="provider_http_error",
message=message,
status_code=502,
retryable=status_code >= 500 or status_code == 429,
) from exc
except APIConnectionError as exc:
raise LLMAdapterError(
code="provider_connection_error",
message=f"Unable to reach {provider} provider endpoint.",
status_code=502,
retryable=True,
) from exc
except Exception as exc: # noqa: BLE001
raise LLMAdapterError(
code="provider_invalid_response",
message=f"{provider} provider returned an unexpected response.",
status_code=502,
retryable=True,
) from exc
choices = getattr(response, "choices", None) or []
if not choices:
raise LLMAdapterError(
code="invalid_provider_response",
message="Provider response did not include choices.",
status_code=502,
retryable=True,
)
content = choices[0].message.content
text = _extract_text_content(content)
if not text:
raise LLMAdapterError(
code="invalid_provider_response",
message="Provider response did not include message content.",
status_code=502,
retryable=True,
)
return text
def _resolve_openai_base_url(base_url: str | None) -> str:
url = (
base_url
or os.getenv("THB_OPENAI_BASE_URL")
or os.getenv("API_BASE_URL")
or "https://api.openai.com/v1"
).rstrip("/")
return _strip_chat_completions_suffix(url)
def _resolve_anthropic_base_url(base_url: str | None) -> str:
url = (
base_url
or os.getenv("THB_ANTHROPIC_BASE_URL")
or "https://api.anthropic.com/v1"
).rstrip("/")
return _strip_chat_completions_suffix(url)
def _resolve_huggingface_base_url(base_url: str | None) -> str:
url = (
base_url
or os.getenv("THB_HF_BASE_URL")
or os.getenv("API_BASE_URL")
or "https://router.huggingface.co/v1"
).rstrip("/")
return _strip_chat_completions_suffix(url)
def _resolve_local_base_url(base_url: str | None) -> str:
raw = (
base_url
or os.getenv("THB_LOCAL_LLM_URL")
or os.getenv("API_BASE_URL")
or "http://localhost:11434/v1"
).rstrip("/")
url = _strip_chat_completions_suffix(raw)
if url.endswith("/api/generate"):
url = f"{url[:-len('/api/generate')]}/v1"
if not url.endswith("/v1"):
url = f"{url}/v1"
return url
def _strip_chat_completions_suffix(url: str) -> str:
if url.endswith("/chat/completions"):
return url[: -len("/chat/completions")]
return url
def _format_http_error(provider: str, status_code: int) -> str:
if status_code in {401, 403}:
return f"Authentication failed for {provider} provider."
if status_code == 404:
return f"Requested model or endpoint was not found for {provider}."
if status_code == 429:
return f"Rate limit exceeded for {provider} provider."
if status_code >= 500:
return f"{provider} provider is temporarily unavailable."
return f"{provider} provider request failed."
def _extract_text_content(content: Any) -> str:
if isinstance(content, str):
return content
if isinstance(content, list):
parts: list[str] = []
for item in content:
if isinstance(item, str):
parts.append(item)
continue
if isinstance(item, dict):
text = item.get("text") or item.get("content")
if isinstance(text, str):
parts.append(text)
return "\n".join(part for part in parts if part).strip()
if isinstance(content, dict):
text = content.get("text") or content.get("content")
if isinstance(text, str):
return text
return ""
def _max_clip_signals_limit() -> int:
raw = os.getenv("THB_MAX_CLIP_SIGNALS_IN_DIGEST", "24").strip()
try:
value = int(raw)
except ValueError:
return 24
return max(1, value)
def _drift_severity(drift: float) -> str:
if drift < 0.05:
return "none"
if drift < 0.12:
return "minor"
if drift < 0.22:
return "moderate"
return "severe"
def _clip_risk_reasons(
*,
drift: float,
blur: float,
lip_sync: float,
lip_sync_reliable: bool,
fallback_used: bool,
) -> tuple[list[str], list[str]]:
high_risk_reasons: list[str] = []
warning_reasons: list[str] = []
if fallback_used:
warning_reasons.append("fallback_extractor_used = true (proxy-only clip signals)")
if drift >= _DRIFT_WARNING_THRESHOLD:
warning_reasons.append("identity_cosine_drift proxy >= 0.22")
if blur <= _BLUR_WARNING_THRESHOLD:
warning_reasons.append("blur_score proxy <= 0.30")
warning_reasons.append("lip_sync_confidence unavailable (fallback_extractor_used)")
return high_risk_reasons, warning_reasons
if drift >= _DRIFT_HIGH_RISK_THRESHOLD:
high_risk_reasons.append("identity_cosine_drift >= 0.35")
elif drift >= _DRIFT_WARNING_THRESHOLD:
warning_reasons.append("identity_cosine_drift >= 0.22")
if blur <= _BLUR_HIGH_RISK_THRESHOLD:
high_risk_reasons.append("blur_score <= 0.20")
elif blur <= _BLUR_WARNING_THRESHOLD:
warning_reasons.append("blur_score <= 0.30")
if lip_sync_reliable:
if lip_sync <= _LIPSYNC_HIGH_RISK_THRESHOLD:
high_risk_reasons.append("lip_sync_confidence <= 0.30")
elif lip_sync <= _LIPSYNC_WARNING_THRESHOLD:
warning_reasons.append("lip_sync_confidence <= 0.40")
else:
warning_reasons.append("lip_sync_confidence unavailable (fallback_extractor_used)")
return high_risk_reasons, warning_reasons
def _clip_quality_tier(
*,
drift: float,
blur: float,
lip_sync: float,
lip_sync_reliable: bool,
fallback_used: bool,
high_risk_reason_count: int,
) -> str:
if fallback_used:
if drift < _DRIFT_WARNING_THRESHOLD and blur >= 0.12:
return "acceptable"
if drift < _DRIFT_HIGH_RISK_THRESHOLD and blur >= 0.08:
return "marginal"
return "poor"
if high_risk_reason_count >= 2:
return "poor"
if high_risk_reason_count == 1:
return "marginal"
is_good = drift < 0.12 and blur > 0.40 and (lip_sync > 0.50 or not lip_sync_reliable)
if is_good:
return "good" if lip_sync_reliable else "acceptable"
is_acceptable = drift < _DRIFT_WARNING_THRESHOLD and blur > 0.25
if is_acceptable and (lip_sync > _LIPSYNC_WARNING_THRESHOLD or not lip_sync_reliable):
return "acceptable"
is_marginal = drift < _DRIFT_HIGH_RISK_THRESHOLD and blur > _BLUR_HIGH_RISK_THRESHOLD
if is_marginal and (lip_sync > _LIPSYNC_HIGH_RISK_THRESHOLD or not lip_sync_reliable):
return "marginal"
return "poor"
def _as_dict(value: Any) -> dict[str, Any]:
return value if isinstance(value, dict) else {}
def _as_list(value: Any) -> list[Any]:
return value if isinstance(value, list) else []
def _as_list_of_dicts(value: Any) -> list[dict[str, Any]]:
if not isinstance(value, list):
return []
return [item for item in value if isinstance(item, dict)]
def _as_float(value: Any, *, default: float = 0.0) -> float:
if isinstance(value, (int, float)):
return float(value)
return default
def _as_int(value: Any, *, default: int = 0) -> int:
if isinstance(value, bool):
return default
if isinstance(value, int):
return value
if isinstance(value, float):
return int(value)
return default
def _as_bool(value: Any, *, default: bool = False) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, str):
cleaned = value.strip().lower()
if cleaned in {"1", "true", "yes", "on"}:
return True
if cleaned in {"0", "false", "no", "off"}:
return False
return default
def _safe_mean(values: list[float]) -> float:
clean = [value for value in values if isinstance(value, (int, float))]
if not clean:
return 0.0
return round(float(sum(clean) / len(clean)), 4)
def _dict_max(values: dict[str, Any]) -> float:
if not values:
return 0.0
return max(_as_float(v) for v in values.values())
def _dict_min(values: dict[str, Any]) -> float:
if not values:
return 0.0
return min(_as_float(v) for v in values.values())
def _phonemes_from_positions(
positions: list[Any],
token_position_to_phoneme: Any,
) -> list[str]:
if not isinstance(token_position_to_phoneme, dict):
return []
mapped: list[str] = []
for position in positions:
idx = _as_int(position, default=-1)
if idx < 0:
continue
key = str(idx)
value = token_position_to_phoneme.get(key)
if value is None and idx in token_position_to_phoneme:
value = token_position_to_phoneme.get(idx)
if isinstance(value, str) and value not in mapped:
mapped.append(value)
if len(mapped) >= 16:
break
return mapped