1"""Image-related document types."""
2
3from __future__ import annotations
4
5from dataclasses import dataclass
6from datetime import datetime
7from pathlib import Path
8from typing import Any
9
10from lexigram.ai.rag.multimodal.types.enums import ImageFormat
11from lexigram.domain import DomainModel
12from lexigram.validation import Field, field_validator
13
14
15@dataclass(init=False)
16class ImageMetadata(DomainModel):
17 """Metadata for image documents."""
18
19 caption: str | None = None
20 alt_text: str | None = None
21 source: str | None = None
22 author: str | None = None
23 created_at: datetime | None = None
24 modified_at: datetime | None = None
25 location: dict[str, float] | None = None
26 tags: list[str] = Field(default_factory=list)
27 exif: dict[str, Any] = Field(default_factory=dict)
28
29 model_config = {
30 "json_schema_extra": {
31 "example": {
32 "caption": "Sunset over mountains",
33 "tags": ["landscape", "sunset"],
34 "source": "https://example.com/image.jpg",
35 },
36 },
37 }
38
39
40@dataclass(init=False)
41class ImageDocument(DomainModel):
42 """Document representing an image."""
43
44 content: bytes | str
45 format: ImageFormat
46 width: int = Field(gt=0)
47 height: int = Field(gt=0)
48 metadata: ImageMetadata = Field(default_factory=ImageMetadata)
49 text_content: str | None = None
50 embedding: list[float] | None = None
51 file_path: Path | None = None
52
53 @field_validator("content")
54 @classmethod
55 def validate_content(cls, v: bytes | str) -> bytes | str:
56 if isinstance(v, str):
57 path = Path(v)
58 if not path.exists():
59 raise ValueError(f"File path does not exist: {v}")
60 return v
61
62 @property
63 def aspect_ratio(self) -> float:
64 return self.width / self.height
65
66 @property
67 def has_text(self) -> bool:
68 return self.text_content is not None and len(self.text_content) > 0
69
70 @property
71 def has_embedding(self) -> bool:
72 return self.embedding is not None and len(self.embedding) > 0
73
74 model_config = {
75 "arbitrary_types_allowed": True,
76 "json_schema_extra": {
77 "example": {
78 "content": "/path/to/image.jpg",
79 "format": "jpeg",
80 "width": 1920,
81 "height": 1080,
82 "metadata": {"caption": "Product photo"},
83 },
84 },
85 }