from __future__ import annotations import re from dataclasses import asdict, dataclass from typing import Iterable from architecture_compatibility import ( EXPLICIT_ARCHITECTURE_FAILURE_ACTION, EXPLICIT_ARCHITECTURE_FAILURE_CATEGORY, EXPLICIT_ARCHITECTURE_FAILURE_REASON, ) @dataclass(frozen=True) class FailureClassification: category: str scope: str action: str deterministic: bool needs_llm: bool reason: str def to_dict(self) -> dict[str, object]: return asdict(self) DETERMINISTIC_POLICIES: dict[str, FailureClassification] = { "PREFLIGHT_OOM": FailureClassification( "memory_capacity", "model_gpu", "reject_if_estimated_load_exceeds_memory", True, False, "structured_oom", ), "MODEL_FILE_NOT_FOUND": FailureClassification( "repository_structure", "model", "require_framework_specific_root_files", True, False, "structured_missing_files", ), "CONTEXT_LENGTH_ERROR": FailureClassification( "context_length", "configuration", "clamp_requested_context_to_model_limit", True, False, "structured_context_limit", ), "DEVICE_OOM": FailureClassification( "runtime_memory", "model_gpu", "lower_memory_risk_or_reject_combination", True, False, "structured_device_oom", ), "STORAGE_ERROR": FailureClassification( "platform_storage", "platform", "retry_later_without_blaming_model", True, False, "structured_storage_error", ), "NO_LOG_PROGRESS": FailureClassification( "platform_stall", "gpu_framework", "open_short_gpu_framework_circuit", True, False, "structured_no_progress", ), } SEMANTIC_POLICIES: dict[str, FailureClassification] = { "MODEL_NOT_SUPPORTED": FailureClassification( "architecture_compatibility", "model_gpu_framework", "consult_profile_history_then_llm", False, True, "framework_version_dependent", ), "MODEL_LOAD_FAILED": FailureClassification( "model_load", "model_gpu_framework", "inspect_root_exception_then_llm_if_unknown", False, True, "broad_load_error", ), "TOKENIZER_FAILED": FailureClassification( "tokenizer_compatibility", "model_framework", "check_tokenizer_files_then_llm", False, True, "broad_tokenizer_error", ), "MISSING_OPERATOR": FailureClassification( "backend_operator", "gpu_framework", "prefer_other_proven_framework_or_gpu", False, True, "backend_version_dependent", ), "ATTENTION_NOT_SUPPORTED": FailureClassification( "attention_backend", "gpu_framework", "prefer_other_proven_framework_or_gpu", False, True, "backend_version_dependent", ), } PLATFORM_PATTERNS = ( (re.compile(r"welcome\.sh: No such file|data: command not found", re.I), "broken_platform_launch_script"), (re.compile(r"id_rsa.*No such file", re.I), "missing_platform_credential"), (re.compile(r"找不到空闲卡|no idle (?:gpu|card)", re.I), "no_idle_device"), (re.compile(r"storage|download.*timed? out|connection reset", re.I), "platform_io_transient"), ) DETERMINISTIC_LOG_PATTERNS = ( (re.compile(r"PREFLIGHT_OOM|out of memory", re.I), "PREFLIGHT_OOM"), (re.compile(r"max_model_len.*greater than.*max_position_embeddings", re.I), "CONTEXT_LENGTH_ERROR"), (re.compile(r"config\.json.*(?:not found|no config)|Invalid repository ID or local directory", re.I), "MODEL_FILE_NOT_FOUND"), ) # These patterns deliberately require both framework and model/architecture # semantics. Generic messages such as "attention backend not supported" or # "GPU type not supported" must not create an architecture blacklist. EXPLICIT_FRAMEWORK_MODEL_UNSUPPORTED_PATTERNS = ( re.compile( r"(?:当前|该|此)(?:推理)?框架.{0,100}(?:不支持|暂不支持|无法支持|不兼容)" r".{0,100}(?:该|此|当前)?(?:模型架构|模型|架构)", re.I, ), re.compile( r"(?:该|此|当前)?(?:模型架构|模型|架构).{0,100}" r"(?:不被|不受).{0,50}(?:当前|该|此)?(?:推理)?框架.{0,30}支持", re.I, ), re.compile( r"\b(?:this|the|current)\s+framework\b.{0,100}" r"\b(?:does\s+not|doesn't|cannot|can't)\s+support\b.{0,100}" r"\b(?:model|model\s+architecture|architecture)\b", re.I, ), re.compile( r"\b(?:model|model\s+architecture|architecture)\b.{0,100}" r"\b(?:is\s+)?not\s+supported\s+by\b.{0,80}\bframework\b", re.I, ), ) MODEL_NOT_SUPPORTED_SUGGESTION_PATTERNS = ( re.compile(r"请(?:更换|换用|使用|选择).{0,50}(?:受支持|支持的)(?:模型|模型架构)", re.I), re.compile( r"\b(?:please\s+)?(?:change|switch|use|choose).{0,60}" r"\b(?:a\s+)?supported\s+(?:model|model\s+architecture)\b", re.I, ), ) def classify_failure_report(report_code: str | None, log_lines: Iterable[str] = ()) -> FailureClassification: code = str(report_code or "").strip().upper() text = "\n".join(str(line) for line in log_lines) # Infrastructure signatures override broad report codes such as # EXECUTE_EMPTY_RESULT so they do not poison model compatibility feedback. for pattern, reason in PLATFORM_PATTERNS: if pattern.search(text): return FailureClassification( "platform_infrastructure", "gpu_framework", "open_short_gpu_framework_circuit_and_retry_other_models", True, False, reason, ) if code in DETERMINISTIC_POLICIES: return DETERMINISTIC_POLICIES[code] for pattern, inferred_code in DETERMINISTIC_LOG_PATTERNS: if pattern.search(text): return DETERMINISTIC_POLICIES[inferred_code] if any(pattern.search(text) for pattern in EXPLICIT_FRAMEWORK_MODEL_UNSUPPORTED_PATTERNS) or ( code == "MODEL_NOT_SUPPORTED" and any(pattern.search(text) for pattern in MODEL_NOT_SUPPORTED_SUGGESTION_PATTERNS) ): return FailureClassification( EXPLICIT_ARCHITECTURE_FAILURE_CATEGORY, "model_gpu_framework", EXPLICIT_ARCHITECTURE_FAILURE_ACTION, True, False, EXPLICIT_ARCHITECTURE_FAILURE_REASON, ) if code in SEMANTIC_POLICIES: return SEMANTIC_POLICIES[code] return FailureClassification( "ambiguous_runtime", "unknown", "send_compact_profile_and_root_exception_to_llm", False, True, code.lower() if code else "missing_structured_error_code", )