Coverage for src / idx_api / models / brokerage_vision_settings.py: 56%
32 statements
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-28 11:09 -0700
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-28 11:09 -0700
1"""Brokerage Vision Settings - per-brokerage AI vision configuration."""
3from typing import TYPE_CHECKING, Optional
5from sqlalchemy import Boolean, ForeignKey, Integer, String, Text
6from sqlalchemy.orm import Mapped, mapped_column, relationship
8from idx_api.models.base import Base, TimestampMixin
10if TYPE_CHECKING:
11 from idx_api.models.brokerage import Brokerage
14class BrokerageVisionSettings(Base, TimestampMixin):
15 """Per-brokerage customization for AI vision analysis.
17 Allows brokerages to customize how property photos are analyzed,
18 what vocabulary to use, and how descriptions are generated.
20 Example use cases:
21 - Mountain region brokerage: emphasize "views", "ski access", "wildlife"
22 - Luxury brokerage: use upscale language, focus on "gourmet kitchen", "spa bath"
23 - Beach/coastal: add "ocean views", "lanai", "hurricane shutters"
24 - Farm/ranch: emphasize "acreage", "barn", "pasture", "water rights"
25 """
27 __tablename__ = "brokerage_vision_settings"
29 id: Mapped[int] = mapped_column(primary_key=True)
30 brokerage_id: Mapped[int] = mapped_column(
31 Integer, ForeignKey("brokerages.id", ondelete="CASCADE"), unique=True, nullable=False
32 )
34 # Feature toggle
35 vision_enabled: Mapped[bool] = mapped_column(Boolean, default=True, server_default="1")
37 # Prompt customization
38 # Additional instructions appended to the base prompt
39 additional_instructions: Mapped[Optional[str]] = mapped_column(
40 Text,
41 comment="Extra instructions for the vision model, e.g., 'Focus on luxury features and use upscale language'"
42 )
44 # Custom vocabulary additions (appended to system default)
45 # Format: "- Category: term1, term2, term3\n- Category2: term4, term5"
46 custom_vocabulary: Mapped[Optional[str]] = mapped_column(
47 Text,
48 comment="Additional feature vocabulary specific to this region/market"
49 )
51 # Priority features to always highlight if visible
52 # Comma-separated list, e.g., "mountain views,ski access,horse property"
53 priority_features: Mapped[Optional[str]] = mapped_column(
54 String(500),
55 comment="Comma-separated features to emphasize if visible"
56 )
58 # Tone/style guidance
59 # Options: "professional", "luxury", "friendly", "technical"
60 description_tone: Mapped[Optional[str]] = mapped_column(
61 String(50),
62 default="professional",
63 comment="Tone for generated descriptions"
64 )
66 # Room type vocabulary overrides (JSON string)
67 # e.g., {"lanai": "covered outdoor living", "bonus_room": "flex space"}
68 room_type_aliases: Mapped[Optional[str]] = mapped_column(
69 Text,
70 comment="JSON mapping of regional room names to standard terms"
71 )
73 # Max photos to analyze per property (override system default)
74 max_photos_per_property: Mapped[Optional[int]] = mapped_column(
75 Integer,
76 comment="Override system default for max photos per property"
77 )
79 # Relationship
80 brokerage: Mapped["Brokerage"] = relationship(back_populates="vision_settings")
82 def __repr__(self) -> str:
83 return f"<BrokerageVisionSettings(brokerage_id={self.brokerage_id}, tone='{self.description_tone}')>"
85 def get_full_vocabulary(self, system_default: str) -> str:
86 """Merge system vocabulary with brokerage custom vocabulary."""
87 if not self.custom_vocabulary:
88 return system_default
89 return f"{system_default}\n{self.custom_vocabulary}"
91 def get_prompt_additions(self) -> str:
92 """Build additional prompt instructions from settings."""
93 parts = []
95 if self.additional_instructions:
96 parts.append(self.additional_instructions)
98 if self.priority_features:
99 features = [f.strip() for f in self.priority_features.split(",")]
100 parts.append(f"Pay special attention to these features if visible: {', '.join(features)}")
102 if self.description_tone and self.description_tone != "professional":
103 tone_guidance = {
104 "luxury": "Use upscale, sophisticated language appropriate for luxury properties.",
105 "friendly": "Use warm, welcoming language that appeals to families.",
106 "technical": "Include precise technical details about materials and construction.",
107 }
108 if self.description_tone in tone_guidance:
109 parts.append(tone_guidance[self.description_tone])
111 return "\n".join(parts) if parts else ""