Coverage for src / idx_api / config.py: 100%
38 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"""API configuration."""
3from pathlib import Path
5from pydantic import Field
6from pydantic_settings import BaseSettings, SettingsConfigDict
9class Settings(BaseSettings):
10 """API settings from environment."""
12 model_config = SettingsConfigDict(
13 env_file=".env",
14 extra="ignore",
15 )
17 # Database (PostgreSQL primary, SQLite for legacy/sync)
18 database_url: str = Field(
19 default="postgresql://mlsgrid:mlsgrid123@localhost:5432/mlsgrid",
20 description="PostgreSQL connection URL"
21 )
22 # Legacy SQLite paths (for sync service and migration)
23 sqlite_db_path: Path = Field(default=Path("/data/mlsgrid.db"))
24 sync_state_db_path: Path = Field(default=Path("/data/sync_state.db"))
26 # Ollama
27 ollama_base_url: str = Field(default="https://ollama.supported.systems")
28 ollama_embed_model: str = Field(default="mxbai-embed-large")
29 embedding_dimensions: int = Field(default=1024) # mxbai-embed-large dimensions
31 # Vision Model (for property photo analysis)
32 vision_model: str = Field(default="qwen3-vl:latest")
33 vision_enabled: bool = Field(default=True)
34 vision_max_photos_per_property: int = Field(default=5)
35 vision_image_max_size: int = Field(default=1024, description="Max image dimension for resizing")
36 vision_request_timeout: float = Field(default=120.0, description="Vision API timeout in seconds")
37 vision_max_retries: int = Field(default=2, description="Max retries for failed vision requests")
38 vision_concurrency: int = Field(default=3, description="Max concurrent vision requests")
40 # Vision Prompt Templates (system-wide defaults, can be overridden per-brokerage)
41 # These use {placeholder} syntax for brokerage customization
42 vision_prompt_base: str = Field(
43 default="""Analyze this real estate photo and extract structured tags.
45Return a JSON object with these fields:
46{{
47 "room_type": "kitchen|bathroom|bedroom|living_room|dining|exterior|backyard|garage|basement|office|other",
48 "features": ["list", "of", "visible", "features"],
49 "materials": ["granite", "hardwood", "tile", "etc"],
50 "style": "modern|traditional|farmhouse|craftsman|contemporary|mid_century|rustic",
51 "condition": "new|updated|original|dated",
52 "highlights": ["buyer-exciting", "features"],
53 "quality_score": 1-5
54}}
56{additional_instructions}
58For features, use searchable terms buyers look for:
59{feature_vocabulary}
61Only include features clearly visible in the photo. Return valid JSON only.""",
62 description="Base vision prompt template with placeholders"
63 )
65 vision_default_vocabulary: str = Field(
66 default="""- Kitchen: island, pantry, breakfast bar, double oven, gas range, granite countertops
67- Bathroom: soaking tub, walk-in shower, double vanity, jetted tub, tile work
68- Living: fireplace, built-ins, vaulted ceiling, open floor plan, hardwood floors
69- Exterior: pool, covered patio, deck, mountain views, workshop, RV parking
70- Garage: shop, workbench, storage, oversized, 3-car""",
71 description="Default feature vocabulary for vision prompts"
72 )
74 # SigLIP Visual Embeddings (Approach B)
75 siglip_enabled: bool = Field(default=True)
76 siglip_base_url: str = Field(default="https://siglip.supported.systems")
77 siglip_dimensions: int = Field(default=1024) # SigLIP-Large-patch16-384 dimensions
79 # Multi-modal search fusion weights (should sum to 1.0)
80 # Tuned based on A/B comparison: text dominates, descriptions add value, visual minimal
81 search_weight_text: float = Field(default=0.50)
82 search_weight_descriptions: float = Field(default=0.45)
83 search_weight_visual: float = Field(default=0.05)
85 # API
86 api_host: str = Field(default="0.0.0.0")
87 api_port: int = Field(default=8000)
88 environment: str = Field(default="dev")
90 # CORS - allow frontend origins
91 cors_origins: list[str] = Field(
92 default=[
93 "http://localhost:4321",
94 "http://localhost:4322",
95 "http://localhost:3000",
96 "https://mlsgrid.l.supported.systems",
97 "https://ei.supported.systems",
98 "http://idx-web:4321", # Docker internal
99 ]
100 )
102 # Keycloak Authentication
103 keycloak_hostname: str = Field(default="keycloak.l.supported.systems")
104 keycloak_realm: str = Field(default="mlsgrid-idx")
105 keycloak_client_id: str = Field(default="idx-web")
106 keycloak_client_secret: str = Field(default="")
108 # JWT Configuration
109 jwt_algorithm: str = Field(default="RS256")
110 jwt_public_key: str = Field(default="")
112 # Domain Verification
113 domain_cname_target: str = Field(
114 default="idx.l.supported.systems",
115 description="Expected CNAME target for domain verification"
116 )
119settings = Settings()