1"""Audio-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 AudioFormat
11from lexigram.domain import DomainModel
12from lexigram.validation import Field, field_validator
13
14
15@dataclass(init=False)
16class AudioMetadata(DomainModel):
17 """Metadata for audio documents."""
18
19 title: str | None = None
20 artist: str | None = None
21 album: str | None = None
22 genre: str | None = None
23 year: int | None = None
24 created_at: datetime | None = None
25 modified_at: datetime | None = None
26 tags: list[str] = Field(default_factory=list)
27 id3: dict[str, Any] = Field(default_factory=dict)
28
29 model_config = {
30 "json_schema_extra": {
31 "example": {
32 "title": "Interview Recording",
33 "artist": "John Doe",
34 "tags": ["interview", "tech"],
35 },
36 },
37 }
38
39
40@dataclass(init=False)
41class AudioDocument(DomainModel):
42 """Document representing audio content."""
43
44 content: bytes | str
45 format: AudioFormat
46 duration: float = Field(gt=0)
47 sample_rate: int = Field(gt=0)
48 channels: int = Field(default=1, ge=1, le=8)
49 metadata: AudioMetadata = Field(default_factory=AudioMetadata)
50 transcript: str | None = None
51 embedding: list[float] | None = None
52 file_path: Path | None = None
53
54 @field_validator("content")
55 @classmethod
56 def validate_content(cls, v: bytes | str) -> bytes | str:
57 if isinstance(v, str):
58 path = Path(v)
59 if not path.exists():
60 raise ValueError(f"File path does not exist: {v}")
61 return v
62
63 @property
64 def has_transcript(self) -> bool:
65 return self.transcript is not None and len(self.transcript) > 0
66
67 @property
68 def has_embedding(self) -> bool:
69 return self.embedding is not None and len(self.embedding) > 0
70
71 @property
72 def is_mono(self) -> bool:
73 return self.channels == 1
74
75 @property
76 def is_stereo(self) -> bool:
77 return self.channels == 2
78
79 model_config = {
80 "arbitrary_types_allowed": True,
81 "json_schema_extra": {
82 "example": {
83 "content": "/path/to/audio.mp3",
84 "format": "mp3",
85 "duration": 180.5,
86 "sample_rate": 44100,
87 "metadata": {"title": "Interview"},
88 },
89 },
90 }