Spaces:
Sleeping
Sleeping
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1 | """Astrazioni del sottosistema di rilevamento (SOLID). | |
| Speculare al sottosistema di rendering PDF: ogni *metodologia di rilevamento* | |
| e' una strategia isolata che dichiara quali riconoscitori attivare. Le | |
| strategie sono registrate in un `DetectionRegistry` ed escludibili da | |
| configurazione: aggiungerne/toglierne una non tocca il codice esistente | |
| (Open/Closed) ne' il livello web (Dependency Inversion). | |
| - Single Responsibility: ogni strategia vive in un proprio file e sa solo | |
| comporre il proprio insieme di riconoscitori. | |
| - Liskov: tutte rispettano `recognizers(provider) -> list[Recognizer]`. | |
| - Interface Segregation: la strategia espone solo `info` e `recognizers`; | |
| il `RecognizerProvider` espone una factory lazy per ogni famiglia di tecniche. | |
| - Dependency Inversion: le strategie dipendono dal Protocol `RecognizerProvider`, | |
| non dai modelli pesanti concreti. | |
| """ | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| from collections.abc import Sequence | |
| from dataclasses import dataclass | |
| from enum import Enum | |
| from typing import Protocol, runtime_checkable | |
| from core.contracts import Recognizer | |
| class DetectionTechnique(str, Enum): | |
| """Famiglia tecnologica di una metodologia di rilevamento.""" | |
| DETERMINISTIC = "deterministic" # regex + checksum, nessun modello | |
| STATISTICAL_NER = "statistical_ner" # token classification (transformer) | |
| SEMANTIC_ZEROSHOT = "zero_shot" # natural language inference | |
| GENERATIVE_LLM = "generative_llm" # LLM istruito che estrae JSON | |
| EMBEDDING = "embedding" # similarita' del coseno su embedding | |
| HYBRID = "hybrid" # fusione multi-tecnica | |
| class StrategyInfo: | |
| """Metadati di una strategia, usati anche dalla UI per i controlli.""" | |
| key: str | |
| label: str | |
| technique: DetectionTechnique | |
| description: str | |
| requires_ml: bool # True se senza modelli degrada al solo backbone | |
| models: tuple[str, ...] = () # id dei modelli usati (informativo per la UI) | |
| default: bool = False | |
| class RecognizerProvider(Protocol): | |
| """Sorgente lazy di riconoscitori per famiglia di tecnica. | |
| Ogni metodo costruisce (una sola volta, con caching a carico | |
| dell'implementazione) i riconoscitori della propria famiglia. In assenza | |
| dei modelli o con ML disabilitato ritorna una sequenza vuota: le strategie | |
| degradano in modo controllato sul backbone a regole. | |
| """ | |
| def rules(self) -> Sequence[Recognizer]: ... | |
| def ner(self) -> Sequence[Recognizer]: ... | |
| def ner_comuni(self) -> Sequence[Recognizer]: ... | |
| def zero_shot(self) -> Sequence[Recognizer]: ... | |
| def baseline(self) -> Sequence[Recognizer]: ... | |
| def llm(self) -> Sequence[Recognizer]: ... | |
| def embedding(self) -> Sequence[Recognizer]: ... | |
| # Costruzione generica per chiave di layer (supporta layer aggiunti a runtime). | |
| def family(self, key: str) -> Sequence[Recognizer]: ... | |
| class DetectionStrategy(ABC): | |
| """Interfaccia comune a tutte le metodologie di rilevamento.""" | |
| #: Metadati statici della strategia. | |
| info: StrategyInfo | |
| def recognizers(self, provider: RecognizerProvider) -> list[Recognizer]: | |
| """Compone l'insieme di riconoscitori della metodologia.""" | |
| def make_strategy(info: StrategyInfo, *provider_methods: str) -> DetectionStrategy: | |
| """Crea una strategia che compone i riconoscitori dei metodi indicati. | |
| `provider_methods` sono i nomi dei metodi del `RecognizerProvider` da | |
| concatenare in ordine (es. "rules", "ner"). Elimina la duplicazione delle | |
| classi-strategia quasi identiche: aggiungere una metodologia diventa una | |
| riga dichiarativa (Open/Closed) invece di un nuovo file. | |
| """ | |
| class _ComposedStrategy(DetectionStrategy): | |
| def recognizers(self, provider: RecognizerProvider) -> list[Recognizer]: | |
| recs: list[Recognizer] = [] | |
| for method in provider_methods: | |
| recs.extend(getattr(provider, method)()) | |
| return recs | |
| strategy = _ComposedStrategy() | |
| strategy.info = info | |
| return strategy | |
| class DetectionRegistry: | |
| """Collezione ordinata di metodologie abilitate (Open/Closed).""" | |
| def __init__(self) -> None: | |
| self._strategies: dict[str, DetectionStrategy] = {} | |
| def register(self, strategy: DetectionStrategy) -> DetectionRegistry: | |
| if strategy.info.key in self._strategies: | |
| raise ValueError(f"Strategia gia' registrata: {strategy.info.key}") | |
| self._strategies[strategy.info.key] = strategy | |
| return self | |
| def has(self, key: str) -> bool: | |
| return key in self._strategies | |
| def get(self, key: str) -> DetectionStrategy | None: | |
| return self._strategies.get(key) | |
| def infos(self) -> list[StrategyInfo]: | |
| return [s.info for s in self._strategies.values()] | |
| def default_key(self) -> str: | |
| for s in self._strategies.values(): | |
| if s.info.default: | |
| return s.info.key | |
| # fallback: la prima registrata | |
| return next(iter(self._strategies)) | |
| def __len__(self) -> int: | |
| return len(self._strategies) | |