fix: harden model catalog projections
This commit is contained in:
+84
-13
@@ -60,8 +60,12 @@ def canonical_effective_config_document(cfg) -> dict:
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"dwh": dwh,
|
||||
"vector": {"collection": collection, "dimensions": 1024, "distance": "cosine"},
|
||||
"embedding": {"model": model, "dimensions": int(embed_dim)},
|
||||
"vector": {"collection": collection, "dimensions": int(embed_dim), "distance": "cosine"},
|
||||
"embedding": {
|
||||
"id": getattr(embeddings, "id", None) or f"ollama/{model}",
|
||||
"model": model,
|
||||
"dimensions": int(embed_dim),
|
||||
},
|
||||
"roots": {
|
||||
"artifacts": str(getattr(cfg.paths, "artifacts", Path("artifacts"))),
|
||||
"indexes": str(getattr(cfg.paths, "indexes", Path("indexes"))),
|
||||
@@ -499,8 +503,9 @@ class EvidenceSourcesConfig(BaseModel):
|
||||
class EmbeddingsConfig(BaseModel):
|
||||
provider: str = "ollama_internal"
|
||||
base_url: str
|
||||
model: str = "nomic-embed-text-v2-moe"
|
||||
dim: int = Field(default=768, alias="dimensions")
|
||||
id: str | None = None
|
||||
model: str
|
||||
dim: int = Field(alias="dimensions")
|
||||
batch_size: int = 16
|
||||
timeout: int = 300
|
||||
connect_timeout: int = 5
|
||||
@@ -642,6 +647,7 @@ def load_config(path: Path) -> Config:
|
||||
if not isinstance(raw, dict):
|
||||
raise ConfigError(f"Configurazione non valida (atteso un mapping YAML): {path}")
|
||||
expanded = _resolve_secret_files(_resolve_evidence_secret_files(_expand_env(raw)))
|
||||
_apply_installation_embedding_projection(expanded, path)
|
||||
_validate_internal_embedding_contract(expanded, path)
|
||||
_validate_internal_vector_contract(expanded, path)
|
||||
translated, used_legacy = translate_legacy_config(expanded)
|
||||
@@ -710,6 +716,56 @@ def load_config(path: Path) -> Config:
|
||||
return cfg
|
||||
|
||||
|
||||
def _apply_installation_embedding_projection(raw: dict[str, Any], path: Path) -> None:
|
||||
projected = {
|
||||
"id": os.environ.get("THT_INTERNAL_EMBEDDING_ID"),
|
||||
"model": os.environ.get("THT_INTERNAL_EMBEDDING_MODEL"),
|
||||
"dimensions": os.environ.get("THT_INTERNAL_EMBEDDING_DIMENSIONS"),
|
||||
}
|
||||
if all(value is None for value in projected.values()):
|
||||
return
|
||||
if any(value is None for value in projected.values()):
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"la proiezione embedding dell'installazione è incompleta"
|
||||
)
|
||||
embedding_id = projected["id"]
|
||||
model = projected["model"]
|
||||
try:
|
||||
dimensions = int(projected["dimensions"] or "")
|
||||
except ValueError:
|
||||
dimensions = 0
|
||||
if embedding_id != f"ollama/{model}" or dimensions <= 0:
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"la proiezione embedding dell'installazione non è canonica"
|
||||
)
|
||||
|
||||
sections: list[tuple[dict[str, Any], str]] = []
|
||||
embeddings = raw.get("embeddings")
|
||||
if isinstance(embeddings, dict):
|
||||
sections.append((embeddings, "dim"))
|
||||
resources = raw.get("resources")
|
||||
if isinstance(resources, dict) and isinstance(resources.get("embeddings"), dict):
|
||||
sections.append((resources["embeddings"], "dimensions"))
|
||||
for section, dimension_key in sections:
|
||||
declared = {
|
||||
"id": section.get("id"),
|
||||
"model": section.get("model"),
|
||||
"dimensions": section.get(dimension_key),
|
||||
}
|
||||
expected = {"id": embedding_id, "model": model, "dimensions": dimensions}
|
||||
for key, value in declared.items():
|
||||
if value is not None and str(value) != str(expected[key]):
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
f"{key} è proprietà dell'installazione e diverge dalla proiezione attiva"
|
||||
)
|
||||
section["id"] = embedding_id
|
||||
section["model"] = model
|
||||
section[dimension_key] = dimensions
|
||||
|
||||
|
||||
def _validate_internal_embedding_contract(raw: dict[str, Any], path: Path) -> None:
|
||||
resources = raw.get("resources")
|
||||
if not isinstance(resources, dict):
|
||||
@@ -720,9 +776,10 @@ def _validate_internal_embedding_contract(raw: dict[str, Any], path: Path) -> No
|
||||
|
||||
provider = embeddings.get("provider")
|
||||
model = embeddings.get("model")
|
||||
embedding_id = embeddings.get("id")
|
||||
dimensions = embeddings.get("dimensions")
|
||||
base_url = embeddings.get("base_url")
|
||||
allowed = {"provider", "base_url", "model", "dimensions"}
|
||||
allowed = {"provider", "base_url", "id", "model", "dimensions"}
|
||||
unexpected = sorted(set(embeddings) - allowed)
|
||||
if unexpected:
|
||||
raise ConfigError(
|
||||
@@ -734,15 +791,24 @@ def _validate_internal_embedding_contract(raw: dict[str, Any], path: Path) -> No
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"resources.embeddings.provider deve essere 'ollama_internal'"
|
||||
)
|
||||
if model != "qwen3-embedding:0.6b":
|
||||
if not isinstance(model, str) or not model:
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"resources.embeddings.model deve essere 'qwen3-embedding:0.6b'"
|
||||
"resources.embeddings.model deve essere valorizzato"
|
||||
)
|
||||
if dimensions != 1024:
|
||||
try:
|
||||
parsed_dimensions = int(dimensions)
|
||||
except (TypeError, ValueError):
|
||||
parsed_dimensions = 0
|
||||
if isinstance(dimensions, bool) or parsed_dimensions <= 0:
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"resources.embeddings.dimensions deve essere 1024"
|
||||
"resources.embeddings.dimensions deve essere un intero positivo"
|
||||
)
|
||||
if embedding_id is not None and embedding_id != f"ollama/{model}":
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"resources.embeddings.id deve essere l'identità canonica ollama/<model>"
|
||||
)
|
||||
if not _is_allowed_internal_embedding_url(base_url):
|
||||
raise ConfigError(
|
||||
@@ -805,15 +871,20 @@ def _validate_active_embeddings_config(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"embeddings.provider deve essere 'ollama_internal'"
|
||||
)
|
||||
if embeddings.model != "qwen3-embedding:0.6b":
|
||||
if not embeddings.model:
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"embeddings.model deve essere 'qwen3-embedding:0.6b'"
|
||||
"embeddings.model deve essere valorizzato"
|
||||
)
|
||||
if embeddings.dim != 1024:
|
||||
if embeddings.dim <= 0:
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"embeddings.dim deve essere 1024"
|
||||
"embeddings.dim deve essere un intero positivo"
|
||||
)
|
||||
if embeddings.id is not None and embeddings.id != f"ollama/{embeddings.model}":
|
||||
raise ConfigError(
|
||||
f"Configurazione non valida in {path}:\n"
|
||||
"embeddings.id deve essere l'identità canonica ollama/<model>"
|
||||
)
|
||||
if not _is_allowed_internal_embedding_url(embeddings.base_url):
|
||||
raise ConfigError(
|
||||
|
||||
Reference in New Issue
Block a user