cinematch 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. cinematch-0.1.0/PKG-INFO +173 -0
  2. cinematch-0.1.0/README.md +143 -0
  3. cinematch-0.1.0/app/__init__.py +2 -0
  4. cinematch-0.1.0/app/cli.py +97 -0
  5. cinematch-0.1.0/app/config.py +38 -0
  6. cinematch-0.1.0/app/data/generate_starter.py +365 -0
  7. cinematch-0.1.0/app/data/starter_catalog.json +3621 -0
  8. cinematch-0.1.0/app/database.py +188 -0
  9. cinematch-0.1.0/app/main.py +1090 -0
  10. cinematch-0.1.0/app/models/schemas.py +124 -0
  11. cinematch-0.1.0/app/services/embeddings.py +115 -0
  12. cinematch-0.1.0/app/services/orchestrator.py +1433 -0
  13. cinematch-0.1.0/app/services/seeder.py +61 -0
  14. cinematch-0.1.0/app/services/taste_dossier.py +276 -0
  15. cinematch-0.1.0/app/services/tmdb.py +472 -0
  16. cinematch-0.1.0/app/static/css/cinematch.css +296 -0
  17. cinematch-0.1.0/app/static/images/placeholder_poster.svg +8 -0
  18. cinematch-0.1.0/app/templates/base.html +258 -0
  19. cinematch-0.1.0/app/templates/chat.html +282 -0
  20. cinematch-0.1.0/app/templates/components/chat_message.html +72 -0
  21. cinematch-0.1.0/app/templates/components/deck_card.html +258 -0
  22. cinematch-0.1.0/app/templates/components/for_you_results.html +78 -0
  23. cinematch-0.1.0/app/templates/components/search_results.html +43 -0
  24. cinematch-0.1.0/app/templates/deck.html +20 -0
  25. cinematch-0.1.0/app/templates/for_you.html +37 -0
  26. cinematch-0.1.0/app/templates/library.html +156 -0
  27. cinematch-0.1.0/app/templates/search.html +29 -0
  28. cinematch-0.1.0/cinematch.egg-info/PKG-INFO +173 -0
  29. cinematch-0.1.0/cinematch.egg-info/SOURCES.txt +40 -0
  30. cinematch-0.1.0/cinematch.egg-info/dependency_links.txt +1 -0
  31. cinematch-0.1.0/cinematch.egg-info/entry_points.txt +2 -0
  32. cinematch-0.1.0/cinematch.egg-info/requires.txt +17 -0
  33. cinematch-0.1.0/cinematch.egg-info/top_level.txt +1 -0
  34. cinematch-0.1.0/pyproject.toml +58 -0
  35. cinematch-0.1.0/setup.cfg +4 -0
  36. cinematch-0.1.0/tests/test_adaptive_drift.py +256 -0
  37. cinematch-0.1.0/tests/test_deck_refill.py +98 -0
  38. cinematch-0.1.0/tests/test_flow.py +73 -0
  39. cinematch-0.1.0/tests/test_for_you_cache.py +105 -0
  40. cinematch-0.1.0/tests/test_pipeline_regressions.py +307 -0
  41. cinematch-0.1.0/tests/test_search_perf.py +32 -0
  42. cinematch-0.1.0/tests/test_why_reasons.py +107 -0
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: cinematch
3
+ Version: 0.1.0
4
+ Summary: A personalized movie and series recommendation web app
5
+ Author-email: bkallash <bayankallash@gmail.com>
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Environment :: Web Environment
8
+ Classifier: Framework :: FastAPI
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: aiofiles>=24.1.0
15
+ Requires-Dist: fastapi>=0.115.0
16
+ Requires-Dist: httpx>=0.27.0
17
+ Requires-Dist: jinja2>=3.1.4
18
+ Requires-Dist: numpy>=1.26.0
19
+ Requires-Dist: openai>=1.40.0
20
+ Requires-Dist: pydantic>=2.8.0
21
+ Requires-Dist: pydantic-settings>=2.4.0
22
+ Requires-Dist: python-dotenv>=1.0.1
23
+ Requires-Dist: python-multipart>=0.0.9
24
+ Requires-Dist: uvicorn[standard]>=0.30.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1.2.2; extra == "dev"
27
+ Requires-Dist: pytest; extra == "dev"
28
+ Requires-Dist: pytest-asyncio; extra == "dev"
29
+ Requires-Dist: twine>=6.1.0; extra == "dev"
30
+
31
+ # CineMatch — AI-Powered Movie & Series Suggestion App
32
+
33
+ A modern, cinephile-grade recommendation engine built in Python, featuring a living **Taste Dossier**, hybrid vector RAG retrieval, and an interactive dark-mode web UI.
34
+
35
+ ---
36
+
37
+ ## Key Features
38
+
39
+ 1. **💬 CineMatch Concierge (Assistant)**:
40
+ - Natural language vibe matching (e.g. *"Suggest a romantic movie like La La Land with bittersweet melancholia"*, *"Cozy 90s holiday comedy"*).
41
+ - Multi-turn conversational flow powered by OpenRouter **GPT-4o**.
42
+ - Dual-layer recommendation pipeline: fast in-process SQLite vector retrieval + LLM reranking against your living **Taste Dossier**.
43
+ - Surfaces personalized reasoning cards with "Why You'll Love It" callouts and highlights matching titles from your own Watchlist.
44
+
45
+ 2. **🎴 Famous Titles Rating Deck**:
46
+ - Fast-fire card queue of widely recognized movies and TV series with posters, trailers, and synopses.
47
+ - Non-neutral **1 to 6 rating scale** (1–3 = Disliked/Flawed, 4–6 = Good/Masterpiece).
48
+ - Adaptive **Aspect Tags** (concise single-concept tags like `Visuals`, `Pacing`, `Ending`, `Soundtrack`, `Plot Holes`, `Cliche`).
49
+ - Quick **"Haven't Seen (Skip)"** and **"Want to Watch"** action buttons.
50
+
51
+ 3. **🔍 Search & Rate**:
52
+ - Real-time debounced search bar connecting local SQLite storage with live TMDB (The Movie Database).
53
+ - Log 1–6 ratings and micro-notes on any title in cinema history.
54
+
55
+ 4. **📚 My Library & Taste Dossier**:
56
+ - **Ratings**: Browse and filter your rated titles by score, media type, and aspect tags.
57
+ - **Watchlist**: Manage everything marked "Want to Watch".
58
+ - **Taste Dossier**: Transparently inspect what the AI has synthesized about your cinematic DNA (Core Loves, Deal-Breakers, Creator Affinities, and Narrative Tropes), with a one-click re-synthesis trigger.
59
+
60
+ ---
61
+
62
+ ## Quickstart Guide
63
+
64
+ ### 1. Activate the Virtual Environment
65
+
66
+ ```powershell
67
+ cd "D:\code Projects\SPOC\movie-suggestion"
68
+ .\venv\Scripts\activate
69
+ ```
70
+
71
+ ### 2. Configure Your API Keys in `.env`
72
+
73
+ Copy `.env.example` to `.env` (or edit existing `.env`):
74
+
75
+ ```env
76
+ # OpenRouter Configuration (for GPT-4o and Text Embeddings)
77
+ OPENROUTER_API_KEY=your_openrouter_api_key_here
78
+ OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
79
+ OPENROUTER_MODEL=openai/gpt-4o
80
+
81
+ # TMDB API Key (Free from https://www.themoviedb.org/settings/api)
82
+ TMDB_API_KEY=your_tmdb_api_key_here
83
+
84
+ # Embeddings
85
+ EMBEDDING_PROVIDER=openrouter
86
+ EMBEDDING_MODEL=openai/text-embedding-3-small
87
+ ```
88
+
89
+ > **Note**: Even before adding API keys, CineMatch runs fully with its pre-bundled catalog of famous titles and deterministic vector fallbacks.
90
+
91
+ Set `EMBEDDING_PROVIDER=local` for offline token-based text matching without a model download. Cloud embeddings provide richer semantic matching. Each stored vector tracks its provider, model, and dimension; startup rebuilds incompatible or older unversioned vectors in batches of 64. With OpenRouter configured, this rebuild makes billable embedding requests. Failed batches are retried at the next startup.
92
+
93
+ ### 3. Run the Web Application
94
+
95
+ ```powershell
96
+ .\venv\Scripts\python.exe -m uvicorn app.main:app --reload --port 8000
97
+ ```
98
+
99
+ Open your browser at: **[http://localhost:8000](http://localhost:8000)**
100
+
101
+ ---
102
+
103
+ ## Install from PyPI
104
+
105
+ Install and run CineMatch from any directory:
106
+
107
+ ```powershell
108
+ python -m pip install cinematch
109
+ cinematch
110
+ ```
111
+
112
+ On the first run, CineMatch prompts for:
113
+
114
+ 1. Your OpenRouter API key. Create one at [OpenRouter Keys](https://openrouter.ai/settings/keys). The key is entered using a hidden password prompt.
115
+ 2. An OpenRouter model identifier, such as `openai/gpt-4o`. Browse the available identifiers on the [OpenRouter Models](https://openrouter.ai/models) page.
116
+
117
+ ```text
118
+ CineMatch first-run setup
119
+ Create an API key at https://openrouter.ai/settings/keys
120
+ OpenRouter API key (input is hidden):
121
+ OpenRouter model [openai/gpt-4o]:
122
+ ```
123
+
124
+ The configuration is saved in CineMatch's operating-system user data directory and reused on later runs. The SQLite database is stored there as well. To change the API key or model later, run:
125
+
126
+ ```powershell
127
+ cinematch --configure
128
+ ```
129
+
130
+ You can also create a `.env` file in the directory where you run CineMatch. This local configuration takes precedence:
131
+
132
+ ```env
133
+ OPENROUTER_API_KEY=pypi_user_openrouter_key
134
+ OPENROUTER_MODEL=openai/gpt-4o
135
+ ```
136
+
137
+ Run `cinematch --help` to select a host, port, or custom data directory.
138
+
139
+ ### Build and publish a release
140
+
141
+ ```powershell
142
+ python -m pip install --upgrade build twine
143
+ python -m build
144
+ python -m twine check dist/*
145
+ python -m twine upload dist/*
146
+ ```
147
+
148
+ PyPI accepts an API token as the username `__token__`; use the full token, including its `pypi-` prefix, as the password. Increase the version in `pyproject.toml` before every later release because PyPI release files cannot be replaced.
149
+
150
+ ---
151
+
152
+ ## Architecture & Design Documents
153
+
154
+ All architectural choices and domain terminology are formally documented:
155
+ - [`CONTEXT.md`](./CONTEXT.md) — Ubiquitous language and domain glossary.
156
+ - [`docs/adr/0001-hybrid-rag-taste-dossier.md`](./docs/adr/0001-hybrid-rag-taste-dossier.md) — Dual-layer RAG & Taste Dossier architecture.
157
+ - [`docs/adr/0002-six-point-forced-choice-rating-scale.md`](./docs/adr/0002-six-point-forced-choice-rating-scale.md) — 1–6 discrete forced-choice rating scale.
158
+ - [`docs/adr/0003-hybrid-catalog-orchestrator.md`](./docs/adr/0003-hybrid-catalog-orchestrator.md) — Local vector search + dynamic TMDB discovery fallback.
159
+ - [`docs/adr/0004-in-process-sqlite-vectors.md`](./docs/adr/0004-in-process-sqlite-vectors.md) — Zero-dependency in-process SQLite BLOB vector storage.
160
+ - [`docs/adr/0005-dual-source-embedding-strategy.md`](./docs/adr/0005-dual-source-embedding-strategy.md) — OpenRouter with offline local embedding toggle.
161
+ - [`docs/adr/0006-bundled-curated-starter-catalog.md`](./docs/adr/0006-bundled-curated-starter-catalog.md) — Bundled curated starter dataset.
162
+ - [`docs/adr/0007-compatible-embeddings.md`](./docs/adr/0007-compatible-embeddings.md) — Embedding compatibility, batched rebuilds, and lightweight local matching.
163
+
164
+ ---
165
+
166
+ ## Verification Tests
167
+
168
+ Install test dependencies and run the suite. Tests use a temporary database and disable external APIs, leaving your library untouched:
169
+
170
+ ```powershell
171
+ .\venv\Scripts\python.exe -m pip install -r requirements-dev.txt
172
+ .\venv\Scripts\python.exe -m pytest -q
173
+ ```
@@ -0,0 +1,143 @@
1
+ # CineMatch — AI-Powered Movie & Series Suggestion App
2
+
3
+ A modern, cinephile-grade recommendation engine built in Python, featuring a living **Taste Dossier**, hybrid vector RAG retrieval, and an interactive dark-mode web UI.
4
+
5
+ ---
6
+
7
+ ## Key Features
8
+
9
+ 1. **💬 CineMatch Concierge (Assistant)**:
10
+ - Natural language vibe matching (e.g. *"Suggest a romantic movie like La La Land with bittersweet melancholia"*, *"Cozy 90s holiday comedy"*).
11
+ - Multi-turn conversational flow powered by OpenRouter **GPT-4o**.
12
+ - Dual-layer recommendation pipeline: fast in-process SQLite vector retrieval + LLM reranking against your living **Taste Dossier**.
13
+ - Surfaces personalized reasoning cards with "Why You'll Love It" callouts and highlights matching titles from your own Watchlist.
14
+
15
+ 2. **🎴 Famous Titles Rating Deck**:
16
+ - Fast-fire card queue of widely recognized movies and TV series with posters, trailers, and synopses.
17
+ - Non-neutral **1 to 6 rating scale** (1–3 = Disliked/Flawed, 4–6 = Good/Masterpiece).
18
+ - Adaptive **Aspect Tags** (concise single-concept tags like `Visuals`, `Pacing`, `Ending`, `Soundtrack`, `Plot Holes`, `Cliche`).
19
+ - Quick **"Haven't Seen (Skip)"** and **"Want to Watch"** action buttons.
20
+
21
+ 3. **🔍 Search & Rate**:
22
+ - Real-time debounced search bar connecting local SQLite storage with live TMDB (The Movie Database).
23
+ - Log 1–6 ratings and micro-notes on any title in cinema history.
24
+
25
+ 4. **📚 My Library & Taste Dossier**:
26
+ - **Ratings**: Browse and filter your rated titles by score, media type, and aspect tags.
27
+ - **Watchlist**: Manage everything marked "Want to Watch".
28
+ - **Taste Dossier**: Transparently inspect what the AI has synthesized about your cinematic DNA (Core Loves, Deal-Breakers, Creator Affinities, and Narrative Tropes), with a one-click re-synthesis trigger.
29
+
30
+ ---
31
+
32
+ ## Quickstart Guide
33
+
34
+ ### 1. Activate the Virtual Environment
35
+
36
+ ```powershell
37
+ cd "D:\code Projects\SPOC\movie-suggestion"
38
+ .\venv\Scripts\activate
39
+ ```
40
+
41
+ ### 2. Configure Your API Keys in `.env`
42
+
43
+ Copy `.env.example` to `.env` (or edit existing `.env`):
44
+
45
+ ```env
46
+ # OpenRouter Configuration (for GPT-4o and Text Embeddings)
47
+ OPENROUTER_API_KEY=your_openrouter_api_key_here
48
+ OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
49
+ OPENROUTER_MODEL=openai/gpt-4o
50
+
51
+ # TMDB API Key (Free from https://www.themoviedb.org/settings/api)
52
+ TMDB_API_KEY=your_tmdb_api_key_here
53
+
54
+ # Embeddings
55
+ EMBEDDING_PROVIDER=openrouter
56
+ EMBEDDING_MODEL=openai/text-embedding-3-small
57
+ ```
58
+
59
+ > **Note**: Even before adding API keys, CineMatch runs fully with its pre-bundled catalog of famous titles and deterministic vector fallbacks.
60
+
61
+ Set `EMBEDDING_PROVIDER=local` for offline token-based text matching without a model download. Cloud embeddings provide richer semantic matching. Each stored vector tracks its provider, model, and dimension; startup rebuilds incompatible or older unversioned vectors in batches of 64. With OpenRouter configured, this rebuild makes billable embedding requests. Failed batches are retried at the next startup.
62
+
63
+ ### 3. Run the Web Application
64
+
65
+ ```powershell
66
+ .\venv\Scripts\python.exe -m uvicorn app.main:app --reload --port 8000
67
+ ```
68
+
69
+ Open your browser at: **[http://localhost:8000](http://localhost:8000)**
70
+
71
+ ---
72
+
73
+ ## Install from PyPI
74
+
75
+ Install and run CineMatch from any directory:
76
+
77
+ ```powershell
78
+ python -m pip install cinematch
79
+ cinematch
80
+ ```
81
+
82
+ On the first run, CineMatch prompts for:
83
+
84
+ 1. Your OpenRouter API key. Create one at [OpenRouter Keys](https://openrouter.ai/settings/keys). The key is entered using a hidden password prompt.
85
+ 2. An OpenRouter model identifier, such as `openai/gpt-4o`. Browse the available identifiers on the [OpenRouter Models](https://openrouter.ai/models) page.
86
+
87
+ ```text
88
+ CineMatch first-run setup
89
+ Create an API key at https://openrouter.ai/settings/keys
90
+ OpenRouter API key (input is hidden):
91
+ OpenRouter model [openai/gpt-4o]:
92
+ ```
93
+
94
+ The configuration is saved in CineMatch's operating-system user data directory and reused on later runs. The SQLite database is stored there as well. To change the API key or model later, run:
95
+
96
+ ```powershell
97
+ cinematch --configure
98
+ ```
99
+
100
+ You can also create a `.env` file in the directory where you run CineMatch. This local configuration takes precedence:
101
+
102
+ ```env
103
+ OPENROUTER_API_KEY=pypi_user_openrouter_key
104
+ OPENROUTER_MODEL=openai/gpt-4o
105
+ ```
106
+
107
+ Run `cinematch --help` to select a host, port, or custom data directory.
108
+
109
+ ### Build and publish a release
110
+
111
+ ```powershell
112
+ python -m pip install --upgrade build twine
113
+ python -m build
114
+ python -m twine check dist/*
115
+ python -m twine upload dist/*
116
+ ```
117
+
118
+ PyPI accepts an API token as the username `__token__`; use the full token, including its `pypi-` prefix, as the password. Increase the version in `pyproject.toml` before every later release because PyPI release files cannot be replaced.
119
+
120
+ ---
121
+
122
+ ## Architecture & Design Documents
123
+
124
+ All architectural choices and domain terminology are formally documented:
125
+ - [`CONTEXT.md`](./CONTEXT.md) — Ubiquitous language and domain glossary.
126
+ - [`docs/adr/0001-hybrid-rag-taste-dossier.md`](./docs/adr/0001-hybrid-rag-taste-dossier.md) — Dual-layer RAG & Taste Dossier architecture.
127
+ - [`docs/adr/0002-six-point-forced-choice-rating-scale.md`](./docs/adr/0002-six-point-forced-choice-rating-scale.md) — 1–6 discrete forced-choice rating scale.
128
+ - [`docs/adr/0003-hybrid-catalog-orchestrator.md`](./docs/adr/0003-hybrid-catalog-orchestrator.md) — Local vector search + dynamic TMDB discovery fallback.
129
+ - [`docs/adr/0004-in-process-sqlite-vectors.md`](./docs/adr/0004-in-process-sqlite-vectors.md) — Zero-dependency in-process SQLite BLOB vector storage.
130
+ - [`docs/adr/0005-dual-source-embedding-strategy.md`](./docs/adr/0005-dual-source-embedding-strategy.md) — OpenRouter with offline local embedding toggle.
131
+ - [`docs/adr/0006-bundled-curated-starter-catalog.md`](./docs/adr/0006-bundled-curated-starter-catalog.md) — Bundled curated starter dataset.
132
+ - [`docs/adr/0007-compatible-embeddings.md`](./docs/adr/0007-compatible-embeddings.md) — Embedding compatibility, batched rebuilds, and lightweight local matching.
133
+
134
+ ---
135
+
136
+ ## Verification Tests
137
+
138
+ Install test dependencies and run the suite. Tests use a temporary database and disable external APIs, leaving your library untouched:
139
+
140
+ ```powershell
141
+ .\venv\Scripts\python.exe -m pip install -r requirements-dev.txt
142
+ .\venv\Scripts\python.exe -m pytest -q
143
+ ```
@@ -0,0 +1,2 @@
1
+ # CineMatch Application Package
2
+ __version__ = "0.1.0"
@@ -0,0 +1,97 @@
1
+ """Command-line entry point for an installed CineMatch application."""
2
+
3
+ import argparse
4
+ import getpass
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from dotenv import load_dotenv, set_key
10
+
11
+ DEFAULT_OPENROUTER_MODEL = "openai/gpt-4o"
12
+
13
+
14
+ def default_data_dir() -> Path:
15
+ if sys.platform == "win32":
16
+ root = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA")
17
+ return Path(root) / "CineMatch" if root else Path.home() / "CineMatch"
18
+ if sys.platform == "darwin":
19
+ return Path.home() / "Library" / "Application Support" / "CineMatch"
20
+ return Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share")) / "cinematch"
21
+
22
+
23
+ def build_parser() -> argparse.ArgumentParser:
24
+ parser = argparse.ArgumentParser(description="Run the CineMatch web application.")
25
+ parser.add_argument("--host", default="127.0.0.1", help="Address to listen on.")
26
+ parser.add_argument("--port", type=int, help="Port to listen on (default: APP_PORT or 8000).")
27
+ parser.add_argument("--reload", action="store_true", help="Reload when source files change.")
28
+ parser.add_argument(
29
+ "--configure",
30
+ action="store_true",
31
+ help="Prompt for the OpenRouter API key and model before starting.",
32
+ )
33
+ parser.add_argument(
34
+ "--data-dir",
35
+ type=Path,
36
+ help="Directory for cinematch.db (default: the operating system's user data directory).",
37
+ )
38
+ return parser
39
+
40
+
41
+ def openrouter_is_configured() -> bool:
42
+ api_key = os.environ.get("OPENROUTER_API_KEY", "").strip()
43
+ model = os.environ.get("OPENROUTER_MODEL", "").strip()
44
+ return bool(api_key and not api_key.startswith("your_") and model)
45
+
46
+
47
+ def configure_openrouter(env_path: Path) -> None:
48
+ if not sys.stdin.isatty():
49
+ raise SystemExit(
50
+ "OpenRouter is not configured. Run 'cinematch --configure' in an interactive "
51
+ "terminal, or set OPENROUTER_API_KEY and OPENROUTER_MODEL."
52
+ )
53
+
54
+ print("\nCineMatch first-run setup")
55
+ print("Create an API key at https://openrouter.ai/settings/keys")
56
+ api_key = getpass.getpass("OpenRouter API key (input is hidden): ").strip()
57
+ if not api_key:
58
+ raise SystemExit("An OpenRouter API key is required.")
59
+
60
+ current_model = os.environ.get("OPENROUTER_MODEL", DEFAULT_OPENROUTER_MODEL)
61
+ model = input(f"OpenRouter model [{current_model}]: ").strip() or current_model
62
+
63
+ env_path.touch(exist_ok=True)
64
+ set_key(env_path, "OPENROUTER_API_KEY", api_key)
65
+ set_key(env_path, "OPENROUTER_MODEL", model)
66
+ os.environ["OPENROUTER_API_KEY"] = api_key
67
+ os.environ["OPENROUTER_MODEL"] = model
68
+ print(f"Configuration saved to {env_path}\n")
69
+
70
+
71
+ def main() -> None:
72
+ args = build_parser().parse_args()
73
+ data_dir = args.data_dir or default_data_dir()
74
+ data_dir.mkdir(parents=True, exist_ok=True)
75
+ user_env_path = data_dir / ".env"
76
+
77
+ # A project-specific .env takes precedence over the saved user configuration.
78
+ load_dotenv(Path.cwd() / ".env")
79
+ load_dotenv(user_env_path)
80
+ if args.configure or not openrouter_is_configured():
81
+ configure_openrouter(user_env_path)
82
+
83
+ os.environ.setdefault("DATABASE_PATH", str(data_dir / "cinematch.db"))
84
+
85
+ from app.config import settings
86
+ import uvicorn
87
+
88
+ uvicorn.run(
89
+ "app.main:app",
90
+ host=args.host,
91
+ port=args.port or settings.APP_PORT,
92
+ reload=args.reload,
93
+ )
94
+
95
+
96
+ if __name__ == "__main__":
97
+ main()
@@ -0,0 +1,38 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Literal
4
+ from pydantic_settings import BaseSettings, SettingsConfigDict
5
+ from pydantic import Field
6
+
7
+ BASE_DIR = Path(__file__).resolve().parent.parent
8
+
9
+ class Settings(BaseSettings):
10
+ # OpenRouter
11
+ OPENROUTER_API_KEY: str = Field(default="", description="OpenRouter API Key")
12
+ OPENROUTER_BASE_URL: str = Field(default="https://openrouter.ai/api/v1", description="OpenRouter Base URL")
13
+ OPENROUTER_MODEL: str = Field(default="openai/gpt-4o", description="Model for reasoning and chat")
14
+
15
+ # Embeddings
16
+ EMBEDDING_PROVIDER: Literal["openrouter", "local"] = "openrouter"
17
+ EMBEDDING_MODEL: str = Field(default="openai/text-embedding-3-small", description="Model for text embeddings")
18
+ EMBEDDING_DIM: int = Field(default=1536, gt=0, description="Dimension of embedding vectors")
19
+
20
+ # TMDB API
21
+ TMDB_API_KEY: str = Field(default="", description="The Movie Database API Key")
22
+ TMDB_BASE_URL: str = Field(default="https://api.themoviedb.org/3", description="TMDB API Base URL")
23
+ TMDB_IMAGE_BASE_URL: str = Field(default="https://image.tmdb.org/t/p/w500", description="TMDB Poster CDN Base URL")
24
+
25
+ # Database
26
+ DATABASE_PATH: Path = Field(default=BASE_DIR / "cinematch.db", description="Path to SQLite database")
27
+
28
+ # App Settings
29
+ APP_PORT: int = Field(default=8000)
30
+ DEBUG: bool = Field(default=True)
31
+
32
+ model_config = SettingsConfigDict(
33
+ env_file=BASE_DIR / ".env",
34
+ env_file_encoding="utf-8",
35
+ extra="ignore"
36
+ )
37
+
38
+ settings = Settings()