Refine AI image context handling

This commit is contained in:
Codex
2026-05-14 18:34:40 +02:00
parent 2a731600e0
commit 86f7827802
10 changed files with 623 additions and 59 deletions

View File

@@ -6,7 +6,11 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from dotenv import load_dotenv
try:
from dotenv import load_dotenv
except ModuleNotFoundError: # pragma: no cover - optional dependency fallback
def load_dotenv(*args: Any, **kwargs: Any) -> bool: # type: ignore[override]
return False
@dataclass(slots=True)
@@ -44,6 +48,14 @@ class AIModelConfig:
description: str | None = None
@dataclass(slots=True)
class AIImageUnderstandConfig:
api_key: str
api_host: str = "https://api.minimax.io"
command: tuple[str, ...] = ("uvx", "minimax-coding-plan-mcp", "-y")
timeout: float | None = None
@dataclass(slots=True)
class AIPromptConfig:
root: Path
@@ -56,6 +68,7 @@ class AIConfig:
default_model: str
context_messages: int
prompt: AIPromptConfig
image_understand: AIImageUnderstandConfig | None = None
providers: dict[str, AIProviderConfig] = field(default_factory=dict)
models: dict[str, AIModelConfig] = field(default_factory=dict)
@@ -87,6 +100,22 @@ def _parse_float(value: Any, *, field_name: str) -> float:
raise ValueError(f"{field_name} must be a number") from exc
def _parse_command_tokens(value: Any, *, field_name: str, default: tuple[str, ...]) -> tuple[str, ...]:
if value is None:
return default
if isinstance(value, str):
tokens = value.strip().split()
if not tokens:
raise ValueError(f"{field_name} must not be empty")
return tuple(tokens)
if isinstance(value, list):
tokens = [str(item).strip() for item in value if str(item).strip()]
if not tokens:
raise ValueError(f"{field_name} must not be empty")
return tuple(tokens)
raise ValueError(f"{field_name} must be a string or a list")
def _parse_mapping(value: Any, *, field_name: str) -> dict[str, Any]:
if value is None:
return {}
@@ -184,6 +213,31 @@ def _parse_ai_config(raw_ai: Any, *, root: Path) -> AIConfig | None:
if context_messages < 0:
raise ValueError("ai.context_messages must be >= 0")
image_understand_raw = _parse_mapping(raw_ai.get("image_understand"), field_name="ai.image_understand")
image_understand: AIImageUnderstandConfig | None = None
if image_understand_raw:
api_key = str(image_understand_raw.get("api_key", "")).strip()
if not api_key:
raise ValueError("ai.image_understand.api_key is required")
api_host = str(image_understand_raw.get("api_host", "https://api.minimax.io")).strip()
if not api_host:
raise ValueError("ai.image_understand.api_host is required")
command = _parse_command_tokens(
image_understand_raw.get("command"),
field_name="ai.image_understand.command",
default=("uvx", "minimax-coding-plan-mcp", "-y"),
)
timeout_value = image_understand_raw.get("timeout")
timeout = None
if timeout_value is not None:
timeout = _parse_float(timeout_value, field_name="ai.image_understand.timeout")
image_understand = AIImageUnderstandConfig(
api_key=api_key,
api_host=api_host,
command=command,
timeout=timeout,
)
providers_raw = _parse_mapping(raw_ai.get("providers"), field_name="ai.providers")
models_raw = _parse_mapping(raw_ai.get("models"), field_name="ai.models")
@@ -239,6 +293,7 @@ def _parse_ai_config(raw_ai: Any, *, root: Path) -> AIConfig | None:
default_model=default_model,
context_messages=context_messages,
prompt=prompt,
image_understand=image_understand,
providers=providers,
models=models,
)