Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-llm/src/lexigram/ai/llm/pinning.py: 50%
24 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
1"""Model revision pinning and policy enforcement (LXF-003)."""
3from __future__ import annotations
5from enum import StrEnum
7from lexigram.ai.llm.exceptions import ModelRevisionMismatchError
9__all__ = [
10 "ModelPinPolicy",
11 "enforce_pin_policy",
12]
15class ModelPinPolicy(StrEnum):
16 """Policy for pinning LLM model revisions.
18 Controls how strictly the configured ``model_revision`` is enforced
19 against the revision returned by the provider.
20 """
22 EXACT = "exact"
23 """Reject any response where the provider's revision does not exactly
24 match the configured revision."""
26 LATEST_MINOR = "latest_minor"
27 """Accept any revision within the same major prefix (everything before
28 the last ``-``). Use when minor patch updates are safe."""
30 LATEST = "latest"
31 """Accept any revision. The provider always returns the latest available
32 revision (default — reproduces current behavior)."""
35def enforce_pin_policy(
36 expected_revision: str | None,
37 actual_revision: str,
38 policy: ModelPinPolicy,
39) -> None:
40 """Enforce a model pin policy.
42 Args:
43 expected_revision: The configured pinned revision, or ``None``.
44 actual_revision: The revision returned by the provider.
45 policy: The pin policy to enforce.
47 Raises:
48 ModelRevisionMismatchError: When the policy is violated.
49 """
50 if policy == ModelPinPolicy.LATEST:
51 return
53 if expected_revision is None:
54 return
56 if policy == ModelPinPolicy.EXACT:
57 if expected_revision != actual_revision:
58 raise ModelRevisionMismatchError(
59 f"Model revision mismatch (EXACT): expected {expected_revision!r}, "
60 f"got {actual_revision!r}"
61 )
63 if policy == ModelPinPolicy.LATEST_MINOR:
64 major_expected = (
65 expected_revision.rsplit("-", 1)[0]
66 if "-" in expected_revision
67 else expected_revision
68 )
69 major_actual = (
70 actual_revision.rsplit("-", 1)[0]
71 if "-" in actual_revision
72 else actual_revision
73 )
74 if major_expected != major_actual:
75 raise ModelRevisionMismatchError(
76 f"Model revision mismatch (LATEST_MINOR): expected major {major_expected!r}, "
77 f"got {major_actual!r}"
78 )