Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-rag/src/lexigram/ai/rag/multimodal/types/image.py: 83%

48 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 07:19 +0800

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 }