Spaces:
Sleeping
Sleeping
File size: 2,808 Bytes
94eb0cb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | """Sprint A14-S54 — garde-fou MRO BaseVLMAdapter (fix audit #6).
Avant S54, l'ordre des parents dans :
class AnthropicVLMAdapter(BaseVLMAdapter, AnthropicAdapter)
était critique mais non vérifié. Un swap accidentel à
``(AnthropicAdapter, BaseVLMAdapter)`` aurait silencieusement donné
output_types = {CORRECTED_TEXT} (depuis LLM) au lieu de {RAW_TEXT}
(depuis VLM) — l'erreur ne se serait manifestée qu'au runtime sur
une jonction de type incompatible.
S54 ajoute ``__init_subclass__`` qui lève ``TypeError`` à la
définition de la classe si l'ordre est incorrect.
"""
from __future__ import annotations
import pytest
from picarones.adapters.llm.anthropic_adapter import AnthropicAdapter
from picarones.adapters.llm.openai_adapter import OpenAIAdapter
from picarones.adapters.vlm import (
AnthropicVLMAdapter,
BaseVLMAdapter,
OpenAIVLMAdapter,
)
from picarones.domain.artifacts import ArtifactType
class TestExistingAdaptersStillValid:
"""Les 4 VLM adapters concrets définis correctement passent."""
def test_anthropic_vlm_defined(self) -> None:
# Si l'ordre était mauvais, l'import aurait planté.
adapter = AnthropicVLMAdapter()
assert adapter.input_types == frozenset({ArtifactType.IMAGE})
assert adapter.output_types == frozenset({ArtifactType.RAW_TEXT})
def test_openai_vlm_defined(self) -> None:
adapter = OpenAIVLMAdapter()
assert adapter.input_types == frozenset({ArtifactType.IMAGE})
class TestWrongOrderRejected:
def test_llm_first_then_vlm_rejected(self) -> None:
"""Définir une classe avec LLM avant VLM doit lever TypeError."""
with pytest.raises(TypeError, match="ordre MRO"):
# Définition dynamique d'une classe avec mauvais ordre.
type(
"BadOrderVLM",
(AnthropicAdapter, BaseVLMAdapter),
{"name": property(lambda self: "bad")},
)
def test_correct_order_accepted(self) -> None:
"""L'ordre correct (VLM en premier) est accepté."""
# Test propriété : aucun TypeError levé.
type(
"GoodOrderVLM",
(BaseVLMAdapter, OpenAIAdapter),
{"name": property(lambda self: "good")},
)
class TestErrorMessageHelpful:
def test_message_explains_the_fix(self) -> None:
with pytest.raises(TypeError) as exc_info:
type(
"BadVLM",
(AnthropicAdapter, BaseVLMAdapter),
{"name": property(lambda self: "x")},
)
msg = str(exc_info.value)
# Le message doit suggérer la correction concrète.
assert "BaseVLMAdapter" in msg
assert "AnthropicAdapter" in msg
assert "Corrigez" in msg or "correct" in msg.lower()
|