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

52 statements  

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

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 }