Pushkar02-n commited on
Commit
f2cb2b4
·
1 Parent(s): 7761651

Few changes for making it production ready

Browse files
src/data_ingestion/saving_data_to_postgres.py → .dockerignore RENAMED
File without changes
Dockerfile ADDED
File without changes
config.py CHANGED
@@ -4,13 +4,11 @@ import os
4
 
5
  class Settings(BaseSettings):
6
  groq_api_key: str
7
- neo4j_uri: str
8
- neo4j_username: str
9
- neo4j_password: str
10
- neo4j_database: str
11
- aura_instanceid: str
12
- aura_instancename: str
13
  model_name: str
 
 
 
 
14
 
15
  class Config:
16
  env_file = '.env'
 
4
 
5
  class Settings(BaseSettings):
6
  groq_api_key: str
 
 
 
 
 
 
7
  model_name: str
8
+ postgres_user: str
9
+ postgres_password: str
10
+ postgres_db: str
11
+ database_url: str
12
 
13
  class Config:
14
  env_file = '.env'
docker-compose.yml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ db:
3
+ image: postgres:15-alpine
4
+ container_name: anime_postgres
5
+ restart: always
6
+ environment:
7
+ POSTGRES_USER: ${POSTGRES_USER}
8
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
9
+ POSTGRES_DB: ${POSTGRES_DB}
10
+ ports:
11
+ - "5433:5432"
12
+ volumes:
13
+ - postgres_data:/var/lib/postgresql/data
14
+
15
+ volumes:
16
+ postgres_data:
pyproject.toml CHANGED
@@ -10,11 +10,12 @@ dependencies = [
10
  "gradio>=6.2.0",
11
  "langchain>=1.1.3",
12
  "langchain-groq>=1.1.1",
13
- "neo4j>=6.1.0",
14
  "pandas>=2.3.3",
 
15
  "python-dotenv>=1.2.1",
16
  "requests>=2.32.5",
17
  "sentence-transformers>=5.2.0",
 
18
  "torch>=2.9.1",
19
  "torchvision>=0.24.1",
20
  "uvicorn>=0.38.0",
 
10
  "gradio>=6.2.0",
11
  "langchain>=1.1.3",
12
  "langchain-groq>=1.1.1",
 
13
  "pandas>=2.3.3",
14
+ "psycopg2-binary>=2.9.11",
15
  "python-dotenv>=1.2.1",
16
  "requests>=2.32.5",
17
  "sentence-transformers>=5.2.0",
18
+ "sqlmodel>=0.0.37",
19
  "torch>=2.9.1",
20
  "torchvision>=0.24.1",
21
  "uvicorn>=0.38.0",
scraper.log ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Critical error on page 41: 504 Server Error: Gateway Time-out for url: https://api.jikan.moe/v4/anime?page=41&limit=25&order_by=popularity&sort=asc
2
+ Saved 1000 anime to data/raw/raw_anime.json
3
+
4
+ Sample anime:
5
+ {
6
+ "mal_id": 16498,
7
+ "url": "https://myanimelist.net/anime/16498/Shingeki_no_Kyojin",
8
+ "images": {
9
+ "jpg": {
10
+ "image_url": "https://cdn.myanimelist.net/images/anime/10/47347.jpg",
11
+ "small_image_url": "https://cdn.myanimelist.net/images/anime/10/47347t.jpg",
12
+ "large_image_url": "https://cdn.myanimelist.net/images/anime/10/47347l.jpg"
13
+ },
14
+ "webp": {
15
+ "image_url": "https://cdn.myanimelist.net/images/anime/10/47347.webp",
16
+ "small_image_url": "https://cdn.myanimelist.net/images/anime/10/47347t.webp",
17
+ "large_image_url": "https://cdn.myanimelist.net/images/anime/10/47347l.webp"
18
+ }
19
+ },
20
+ "title": "Shingeki no Kyojin",
21
+ "title_english": "Attack on Titan",
22
+ "synopsis": "Centuries ago, mankind was slaughtered to near extinction by monstrous humanoid creatures called Titans, forcing humans to hide in fear behind enormous concentric walls. What makes these giants truly terrifying is that their taste for human flesh is not born out of hunger but what appears to be out of pleasure. To ensure their survival, the remnants of humanity began living within defensive barriers, resulting in one hundred years without a single titan encounter. However, that fragile calm is soon shattered when a colossal Titan manages to breach the supposedly impregnable outer wall, reigniting the fight for survival against the man-eating abominations.\n\nAfter witnessing a horrific personal loss at the hands of the invading creatures, Eren Yeager dedicates his life to their eradication by enlisting into the Survey Corps, an elite military unit that combats the merciless humanoids outside the protection of the walls. Eren, his adopted sister Mikasa Ackerman, and his childhood friend Armin Arlert join the brutal war against the Titans and race to discover a way of defeating them before the last walls are breached.\n\n[Written by MAL Rewrite]",
23
+ "genres": [
24
+ "Action",
25
+ "Award Winning",
26
+ "Drama",
27
+ "Suspense"
28
+ ],
29
+ "studios": [
30
+ "Wit Studio"
31
+ ],
32
+ "themes": [
33
+ "Gore",
34
+ "Military",
35
+ "Survival"
36
+ ],
37
+ "demographics": [
38
+ "Shounen"
39
+ ],
40
+ "type": "TV",
41
+ "episodes": 25,
42
+ "score": 8.57,
43
+ "scored_by": 3036922,
44
+ "rank": 120,
45
+ "popularity": 1,
46
+ "year": 2013,
47
+ "rating": "R - 17+ (violence & profanity)",
48
+ "season": "spring",
49
+ "aired_from": "2013-04-07T00:00:00+00:00",
50
+ "aired_to": "2013-09-29T00:00:00+00:00",
51
+ "favorites": 187237
52
+ }
src/data_ingestion/clean_data.py CHANGED
@@ -3,6 +3,7 @@ import pandas as pd
3
  import logging
4
 
5
  logger = logging.getLogger(__name__)
 
6
 
7
 
8
  class AnimeDataCleaner:
@@ -89,19 +90,33 @@ class AnimeDataCleaner:
89
  for anime in anime_list:
90
  record = {
91
  "mal_id": anime["mal_id"],
92
- "title": anime["title"],
93
- "searchable_text": AnimeDataCleaner.create_searchable_text(anime),
94
- # Rest is metadata for filtering and display
95
- "genres": ", ".join(anime.get("genres", [])),
96
- "score": anime.get("score"),
97
- "episodes": anime.get("episodes"),
 
 
 
 
 
 
98
  "type": anime.get("type"),
 
 
 
 
 
99
  "year": anime.get("year"),
100
- "synopsis": AnimeDataCleaner.clean_synopsis(anime.get("synopsis", "")),
101
- "aired_from": anime.get("aired_from", ""),
102
- "aired_to": anime.get("aired_to", ""),
103
  "rating": anime.get("rating"),
104
- "scored_by": anime.get("scored_by"),
 
 
 
 
 
105
  }
106
 
107
  records.append(record)
@@ -125,14 +140,15 @@ if __name__ == "__main__":
125
  cleaner = AnimeDataCleaner()
126
 
127
  print("Loading raw data....")
128
- raw_animes = cleaner.load_raw_data()
129
 
130
  valid_animes = cleaner.filter_valid_anime(raw_animes)
131
 
132
  print("\nPreparing data for embedding...")
133
  df = cleaner.prepare_for_embedding(valid_animes)
134
 
135
- cleaner.save_processed_data(df)
 
136
 
137
  print("\nSample searchable text:")
138
  print(df.iloc[0]["searchable_text"][:500])
 
3
  import logging
4
 
5
  logger = logging.getLogger(__name__)
6
+ logging.basicConfig(level=logging.INFO)
7
 
8
 
9
  class AnimeDataCleaner:
 
90
  for anime in anime_list:
91
  record = {
92
  "mal_id": anime["mal_id"],
93
+ "url": anime.get("url"),
94
+ "title": anime.get("title"),
95
+ "title_english": anime.get("title_english"),
96
+ "synopsis": AnimeDataCleaner.clean_synopsis(anime.get("synopsis", "")),
97
+
98
+ # Keep these as native dicts/lists for Postgres JSONB!
99
+ "images": anime.get("images", {}),
100
+ "genres": anime.get("genres", []),
101
+ "studios": anime.get("studios", []),
102
+ "themes": anime.get("themes", []),
103
+ "demographics": anime.get("demographics", []),
104
+
105
  "type": anime.get("type"),
106
+ "episodes": anime.get("episodes"),
107
+ "score": anime.get("score"),
108
+ "scored_by": anime.get("scored_by"),
109
+ "rank": anime.get("rank"),
110
+ "popularity": anime.get("popularity"),
111
  "year": anime.get("year"),
112
+ "season": anime.get("season"),
 
 
113
  "rating": anime.get("rating"),
114
+ "aired_from": anime.get("aired_from"),
115
+ "aired_to": anime.get("aired_to"),
116
+ "favorites": anime.get("favorites"),
117
+
118
+ # And our custom RAG field
119
+ "searchable_text": AnimeDataCleaner.create_searchable_text(anime)
120
  }
121
 
122
  records.append(record)
 
140
  cleaner = AnimeDataCleaner()
141
 
142
  print("Loading raw data....")
143
+ raw_animes = cleaner.load_raw_data("data/raw/raw_anime.json")
144
 
145
  valid_animes = cleaner.filter_valid_anime(raw_animes)
146
 
147
  print("\nPreparing data for embedding...")
148
  df = cleaner.prepare_for_embedding(valid_animes)
149
 
150
+ cleaner.save_processed_data(
151
+ df, filepath="data/processed/anime_clean.csv")
152
 
153
  print("\nSample searchable text:")
154
  print(df.iloc[0]["searchable_text"][:500])
src/data_ingestion/fetch_anime.py CHANGED
@@ -11,6 +11,8 @@ def convert_datetime(dt: str | None):
11
  return datetime.fromisoformat(dt)
12
 
13
 
 
 
14
  logger = logging.getLogger(__name__)
15
 
16
 
@@ -22,55 +24,94 @@ class AnimeDataFetcher:
22
  def __init__(self):
23
  self.session = requests.Session()
24
 
25
- def fetch_top_anime(self, limit: int = 100) -> list[dict]:
26
  """
27
- Fetches top 'limit' animes from MyAnimeLists
28
- Args:
29
- limit: Number of anime to fetch(max ~500 with pagination)
30
- Returns:
31
- List of anime dictionaries
32
  """
33
-
34
  all_animes = []
35
- page = 1
36
- per_page = 25
37
-
38
- while len(all_animes) < limit:
39
- try:
40
- response = self.session.get(
41
- f"{self.BASE_URL}top/anime",
42
- params={"page": page, "limit": per_page}
43
- )
44
- response.raise_for_status()
45
- logger.info(
46
- f"Response received successfully: {response.status_code} !!")
47
-
48
- data = response.json()
49
- anime_list = data.get("data", [])
50
- if not anime_list:
51
- break
52
-
53
- all_animes.extend(anime_list)
54
- logger.info(
55
- f"Fetched page {page}: {len(anime_list)} animes. Total: {len(all_animes)}")
56
-
57
- page += 1
58
- time.sleep(1.5)
59
-
60
- except Exception as e:
61
- logger.error(f"Error fetching page {page}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  break
63
 
64
- return all_animes[:limit]
65
 
66
  def extract_relevant_fields(self, anime: dict) -> dict:
67
  """Extract only fields we need for RAG"""
68
  return {
69
  "mal_id": anime.get("mal_id"),
 
 
70
  "title": anime.get("title"),
71
  "title_english": anime.get("title_english"),
72
  "synopsis": anime.get("synopsis"),
73
  "genres": [g["name"] for g in anime.get("genres", [])],
 
74
  "themes": [t["name"] for t in anime.get("themes", [])],
75
  "demographics": [d["name"] for d in anime.get("demographics", [])],
76
  "type": anime.get("type"),
@@ -98,12 +139,13 @@ class AnimeDataFetcher:
98
  if __name__ == "__main__":
99
  fetcher = AnimeDataFetcher()
100
 
101
- logger.info("Fetching top anime from MyAnimeList...")
102
- raw_anime = fetcher.fetch_top_anime(limit=1000)
103
 
104
  processed_anime = [fetcher.extract_relevant_fields(a) for a in raw_anime]
105
 
106
- fetcher.save_raw_data(processed_anime)
 
107
 
108
  print("\nSample anime: ")
109
  print(json.dumps(processed_anime[0], indent=2, ensure_ascii=False))
 
11
  return datetime.fromisoformat(dt)
12
 
13
 
14
+ logging.basicConfig(level=logging.INFO,
15
+ format='%(asctime)s - %(levelname)s - %(message)s')
16
  logger = logging.getLogger(__name__)
17
 
18
 
 
24
  def __init__(self):
25
  self.session = requests.Session()
26
 
27
+ def fetch_bulk_anime(self, total_limit: int = 10000, filename: str = "raw_anime.json"):
28
  """
29
+ Fetches anime in bulk with Resume and Retry capabilities.
 
 
 
 
30
  """
31
+ filepath = f"data/raw/{filename}"
32
  all_animes = []
33
+
34
+ # --- RESUME LOGIC ---
35
+ try:
36
+ with open(filepath, "r", encoding="utf-8") as f:
37
+ all_animes = json.load(f)
38
+ logger.info(
39
+ f"Found existing data. Resuming from record {len(all_animes)}.")
40
+ except (FileNotFoundError, json.JSONDecodeError):
41
+ logger.info("No existing data found. Starting fresh.")
42
+
43
+ # Calculate the next page to fetch (25 items per page)
44
+ page = (len(all_animes) // 25) + 1
45
+ max_retries = 5
46
+
47
+ while len(all_animes) < total_limit:
48
+ retries = 0
49
+ success = False
50
+
51
+ while retries < max_retries and not success:
52
+ try:
53
+ logger.info(
54
+ f"🚀 Fetching page {page} (Progress: {len(all_animes)}/{total_limit})...")
55
+ response = self.session.get(
56
+ f"{self.BASE_URL}anime",
57
+ params={
58
+ "page": page,
59
+ "limit": 25,
60
+ "order_by": "popularity",
61
+ "sort": "asc"
62
+ },
63
+ timeout=30
64
+ )
65
+
66
+ if response.status_code == 429:
67
+ wait = 60 + (retries * 30) # Increasing wait time
68
+ logger.warning(f"⚠️ Rate limit! Sleeping {wait}s...")
69
+ time.sleep(wait)
70
+ retries += 1
71
+ continue
72
+
73
+ response.raise_for_status()
74
+ data = response.json()
75
+ anime_list = data.get("data", [])
76
+
77
+ if not anime_list:
78
+ logger.info("🏁 No more anime found in API.")
79
+ return all_animes
80
+
81
+ all_animes.extend(anime_list)
82
+
83
+ # --- AUTO-SAVE EVERY PAGE ---
84
+ # In production, we save often so we never lose more than 1 page of work
85
+ self.save_raw_data(all_animes, filename=filename)
86
+
87
+ success = True
88
+ page += 1
89
+ time.sleep(1.2) # Polite delay
90
+
91
+ except Exception as e:
92
+ retries += 1
93
+ logger.error(
94
+ f"❌ Error on page {page}: {e}. Retry {retries}/{max_retries}")
95
+ time.sleep(10 * retries)
96
+
97
+ if not success:
98
+ logger.critical(
99
+ f"🛑 Giving up on page {page}. Run script again later to resume.")
100
  break
101
 
102
+ return all_animes[:total_limit]
103
 
104
  def extract_relevant_fields(self, anime: dict) -> dict:
105
  """Extract only fields we need for RAG"""
106
  return {
107
  "mal_id": anime.get("mal_id"),
108
+ "url": anime.get("url", ""),
109
+ "images": anime.get("images", {}),
110
  "title": anime.get("title"),
111
  "title_english": anime.get("title_english"),
112
  "synopsis": anime.get("synopsis"),
113
  "genres": [g["name"] for g in anime.get("genres", [])],
114
+ "studios": [s["name"] for s in anime.get("studios", [])],
115
  "themes": [t["name"] for t in anime.get("themes", [])],
116
  "demographics": [d["name"] for d in anime.get("demographics", [])],
117
  "type": anime.get("type"),
 
139
  if __name__ == "__main__":
140
  fetcher = AnimeDataFetcher()
141
 
142
+ logger.info("Fetching top 10000 anime from MyAnimeList...")
143
+ raw_anime = fetcher.fetch_bulk_anime(total_limit=10000)
144
 
145
  processed_anime = [fetcher.extract_relevant_fields(a) for a in raw_anime]
146
 
147
+ fetcher.save_raw_data(processed_anime,
148
+ filename="raw_anime.json")
149
 
150
  print("\nSample anime: ")
151
  print(json.dumps(processed_anime[0], indent=2, ensure_ascii=False))
src/data_ingestion/load_to_neo4j.py DELETED
@@ -1,24 +0,0 @@
1
- from neo4j import GraphDatabase
2
- import pandas as pd
3
- from src.utils.neo4j_client import Neo4j_Client
4
-
5
- class AnimeGraphLoader:
6
- """Loads anime into Neo4j Graph"""
7
-
8
- def __init__(self):
9
- self.client = Neo4j_Client()
10
-
11
- def clear_database(self):
12
- """Delete all nodes (use carefully !)"""
13
- query = "MATCH (n) DETACH DELETE n"
14
- self.client.run_query(query)
15
-
16
- def create_constraints(self):
17
- """Create uniqueness constraints for performance"""
18
- queries = [
19
- "CREATE CONSTRAINT anime_id IF NOT EXISTS FOR (a: Anime) REQUIRE a.mal_id IS UNIQUE",
20
- "CREATE CONSTRAINT genre_name IF NOT EXISTS FOR (g:Genre) REQUIRE g.name IS UNIQUE",
21
- "CREATE CONSTRAINT theme_name IF NOT EXISTS FOR (t:Theme) REQUIRE t.name IS UNIQUE",
22
- "CREATE CONSTRAINT demographic_name IF NOT EXISTS FOR (d:Demographic) REQUIRE d.name IS UNIQUE",
23
- "CREAE CONSTRAINT content_type IF NOT EXISTS FOR (t: Type) REQUIRE t.type IS UNIUE"
24
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/data_ingestion/load_to_postgres.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ from sqlmodel import Session, select
4
+ from src.database.session import engine
5
+ from src.database.models import Animes
6
+ from src.database import init_db
7
+
8
+ init_db()
9
+
10
+ logging.basicConfig(level=logging.WARNING)
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def load_json_data(filepath: str = "data/processed/anime_clean.json") -> list[dict]:
15
+ """Reads the cleaned JSON file."""
16
+ with open(filepath, 'r', encoding='utf-8') as f:
17
+ return json.load(f)
18
+
19
+
20
+ def insert_animes_to_db(anime_list: list[dict]):
21
+ """
22
+ Inject a list of anime dictionaries into PostgreSQL safely
23
+ """
24
+ inserted_count = 0
25
+ skipped_count = 0
26
+ with Session(engine) as session:
27
+ for data in anime_list:
28
+ try:
29
+ existing = session.exec(
30
+ select(Animes).where(Animes.mal_id == data["mal_id"])
31
+ ).first()
32
+
33
+ if not existing:
34
+ new_anime = Animes(**data)
35
+ session.add(new_anime)
36
+ inserted_count += 1
37
+ print(f"Inserted_count: {inserted_count}/{len(anime_list)} animes")
38
+ else:
39
+ skipped_count += 1
40
+
41
+ except Exception as e:
42
+ logger.error(
43
+ f"Error processing anime ID: {data.get("mal_id")}: {e}")
44
+
45
+ session.commit()
46
+ logger.info(
47
+ f"Injection complete! Inserted: {inserted_count} | Skipped (Duplicates): {skipped_count}")
48
+
49
+
50
+ if __name__ == "__main__":
51
+ anime_data = load_json_data(filepath="data/processed/anime_clean.json")
52
+
53
+ insert_animes_to_db(anime_data)
src/database/__init__.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from sqlmodel import SQLModel
3
+ from src.database.session import engine
4
+ from src.database.models import Animes, User
5
+
6
+ logging.basicConfig(level=logging.INFO)
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def init_db():
11
+ """Connects to PostgreSQL DB and creates all tables defined in SQLModel classes"""
12
+ logger.info("Started table creation....")
13
+ try:
14
+ SQLModel.metadata.create_all(engine)
15
+ logger.info("Database Tables created successfully")
16
+ except Exception as e:
17
+ logger.error(f"Failed to create tables: {e}")
src/database/crud.py ADDED
File without changes
src/database/models.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlmodel import SQLModel, Field, TIMESTAMP, text
2
+ from sqlalchemy import Column
3
+ from sqlalchemy.dialects.postgresql import JSONB
4
+ from datetime import datetime, timezone
5
+
6
+
7
+ class Animes(SQLModel, table=True):
8
+ id: int | None = Field(default=None, primary_key=True)
9
+
10
+ mal_id: int = Field(index=True, unique=True)
11
+ url: str | None = Field(default=None)
12
+
13
+ title: str = Field(index=True)
14
+ title_english: str | None = Field(default=None)
15
+ searchable_text: str | None = Field(default=None)
16
+
17
+ synopsis: str | None = Field(default=None)
18
+
19
+ # Store nested JSON
20
+ images: dict = Field(sa_column=Column(JSONB))
21
+
22
+ # Arrays → Postgres ARRAY or JSON
23
+ genres: list[str] = Field(sa_column=Column(JSONB))
24
+ studios: list[str] = Field(sa_column=Column(JSONB))
25
+ themes: list[str] = Field(sa_column=Column(JSONB))
26
+ demographics: list[str] = Field(sa_column=Column(JSONB))
27
+
28
+ type: str | None = None
29
+ episodes: int | None = None
30
+
31
+ score: float | None = Field(default=None, index=True)
32
+ scored_by: int | None = None
33
+
34
+ rank: int | None = Field(default=None, index=True)
35
+ popularity: int | None = None
36
+
37
+ year: int | None = Field(default=None, index=True)
38
+ season: str | None = None
39
+
40
+ rating: str | None = None
41
+
42
+ aired_from: str | None = None
43
+ aired_to: str | None = None
44
+
45
+ favorites: int | None = None
46
+
47
+
48
+ class User(SQLModel, table=True):
49
+ id: int | None = Field(default=None, primary_key=True)
50
+ email: str = Field(unique=True, index=True)
51
+ hashed_password: str
52
+
53
+ created_at: datetime | None = Field(sa_column=Column(TIMESTAMP(timezone=True),
54
+ nullable=True,
55
+ server_default=text("NOW()")))
56
+ is_active: bool = Field(default=True)
src/database/session.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from sqlmodel import create_engine
2
+ import os
3
+ from config import settings
4
+
5
+ engine = create_engine(settings.database_url, echo=True)
src/utils/neo4j_client.py DELETED
@@ -1,49 +0,0 @@
1
- from dotenv import load_dotenv
2
- from neo4j import GraphDatabase
3
-
4
- from config import settings
5
-
6
-
7
- class Neo4j_Client:
8
- """Neo4j database client"""
9
-
10
- def __init__(self):
11
- uri = settings.neo4j_uri
12
- user = settings.neo4j_username
13
- password = settings.neo4j_password
14
- try:
15
- self.driver = GraphDatabase.driver(
16
- uri, auth=(user, password)) # type: ignore
17
- self.driver.verify_connectivity()
18
-
19
- except Exception as e:
20
- raise e
21
-
22
- def close(self):
23
- self.driver.close()
24
-
25
- def verify_connectivity(self):
26
- """Test connection"""
27
- with self.driver.session() as session:
28
- result = session.run("RETURN 'Connection Successful !' AS message")
29
- record = result.single()
30
- if record['message']: #type: ignore
31
- return record["message"] # type: ignore
32
- else:
33
- return "Connection failed"
34
-
35
- def run_query(self, query, parameters=None):
36
- with self.driver.session() as session:
37
- result = session.run(query, parameters or {})
38
- return [record.data() for record in result]
39
-
40
-
41
- if __name__ == '__main__':
42
- client = Neo4j_Client()
43
-
44
- message = client.verify_connectivity()
45
- print(f"Message: {message}")
46
-
47
- result = client.run_query("RETURN 1 + 1 AS result")
48
- print(f"Test query result: {result}")
49
- client.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
uv.lock CHANGED
@@ -32,11 +32,12 @@ dependencies = [
32
  { name = "gradio" },
33
  { name = "langchain" },
34
  { name = "langchain-groq" },
35
- { name = "neo4j" },
36
  { name = "pandas" },
 
37
  { name = "python-dotenv" },
38
  { name = "requests" },
39
  { name = "sentence-transformers" },
 
40
  { name = "torch", version = "2.9.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
41
  { name = "torch", version = "2.9.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
42
  { name = "torchvision", version = "0.24.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
@@ -58,11 +59,12 @@ requires-dist = [
58
  { name = "gradio", specifier = ">=6.2.0" },
59
  { name = "langchain", specifier = ">=1.1.3" },
60
  { name = "langchain-groq", specifier = ">=1.1.1" },
61
- { name = "neo4j", specifier = ">=6.1.0" },
62
  { name = "pandas", specifier = ">=2.3.3" },
 
63
  { name = "python-dotenv", specifier = ">=1.2.1" },
64
  { name = "requests", specifier = ">=2.32.5" },
65
  { name = "sentence-transformers", specifier = ">=5.2.0" },
 
66
  { name = "torch", specifier = ">=2.9.1", index = "https://download.pytorch.org/whl/cpu" },
67
  { name = "torchvision", specifier = ">=0.24.1", index = "https://download.pytorch.org/whl/cpu" },
68
  { name = "uvicorn", specifier = ">=0.38.0" },
@@ -950,6 +952,38 @@ wheels = [
950
  { url = "https://files.pythonhosted.org/packages/a7/a2/a3497afea984202f481de3464b3bfbcb3de1cd83cbbf3714933d40dd7106/gradio_client-2.0.2-py3-none-any.whl", hash = "sha256:46a7f63eaa7758fe2e38be7f78f26a1fff48a7b526ebdd87141b050e08556622", size = 55566, upload-time = "2025-12-19T18:29:47.508Z" },
951
  ]
952
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
953
  [[package]]
954
  name = "groovy"
955
  version = "0.1.2"
@@ -1942,18 +1976,6 @@ wheels = [
1942
  { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" },
1943
  ]
1944
 
1945
- [[package]]
1946
- name = "neo4j"
1947
- version = "6.1.0"
1948
- source = { registry = "https://pypi.org/simple" }
1949
- dependencies = [
1950
- { name = "pytz" },
1951
- ]
1952
- sdist = { url = "https://files.pythonhosted.org/packages/1b/01/d6ce65e4647f6cb2b9cca3b813978f7329b54b4e36660aaec1ddf0ccce7a/neo4j-6.1.0.tar.gz", hash = "sha256:b5dde8c0d8481e7b6ae3733569d990dd3e5befdc5d452f531ad1884ed3500b84", size = 239629, upload-time = "2026-01-12T11:27:34.777Z" }
1953
- wheels = [
1954
- { url = "https://files.pythonhosted.org/packages/70/5c/ee71e2dd955045425ef44283f40ba1da67673cf06404916ca2950ac0cd39/neo4j-6.1.0-py3-none-any.whl", hash = "sha256:3bd93941f3a3559af197031157220af9fd71f4f93a311db687bd69ffa417b67d", size = 325326, upload-time = "2026-01-12T11:27:33.196Z" },
1955
- ]
1956
-
1957
  [[package]]
1958
  name = "nest-asyncio"
1959
  version = "1.6.0"
@@ -2511,6 +2533,47 @@ wheels = [
2511
  { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
2512
  ]
2513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2514
  [[package]]
2515
  name = "ptyprocess"
2516
  version = "0.7.0"
@@ -3535,6 +3598,59 @@ wheels = [
3535
  { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
3536
  ]
3537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3538
  [[package]]
3539
  name = "stack-data"
3540
  version = "0.6.3"
@@ -3671,10 +3787,10 @@ dependencies = [
3671
  { name = "typing-extensions", marker = "sys_platform == 'darwin'" },
3672
  ]
3673
  wheels = [
3674
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp311-none-macosx_11_0_arm64.whl" },
3675
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp312-none-macosx_11_0_arm64.whl" },
3676
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp313-cp313t-macosx_11_0_arm64.whl" },
3677
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp313-none-macosx_11_0_arm64.whl" },
3678
  ]
3679
 
3680
  [[package]]
@@ -3699,21 +3815,21 @@ dependencies = [
3699
  { name = "typing-extensions", marker = "sys_platform != 'darwin'" },
3700
  ]
3701
  wheels = [
3702
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-manylinux_2_28_aarch64.whl" },
3703
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-manylinux_2_28_x86_64.whl" },
3704
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-win_amd64.whl" },
3705
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-win_arm64.whl" },
3706
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl" },
3707
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl" },
3708
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-win_amd64.whl" },
3709
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-win_arm64.whl" },
3710
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl" },
3711
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl" },
3712
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-win_amd64.whl" },
3713
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-win_arm64.whl" },
3714
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl" },
3715
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl" },
3716
- { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-win_amd64.whl" },
3717
  ]
3718
 
3719
  [[package]]
 
32
  { name = "gradio" },
33
  { name = "langchain" },
34
  { name = "langchain-groq" },
 
35
  { name = "pandas" },
36
+ { name = "psycopg2-binary" },
37
  { name = "python-dotenv" },
38
  { name = "requests" },
39
  { name = "sentence-transformers" },
40
+ { name = "sqlmodel" },
41
  { name = "torch", version = "2.9.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
42
  { name = "torch", version = "2.9.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
43
  { name = "torchvision", version = "0.24.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
 
59
  { name = "gradio", specifier = ">=6.2.0" },
60
  { name = "langchain", specifier = ">=1.1.3" },
61
  { name = "langchain-groq", specifier = ">=1.1.1" },
 
62
  { name = "pandas", specifier = ">=2.3.3" },
63
+ { name = "psycopg2-binary", specifier = ">=2.9.11" },
64
  { name = "python-dotenv", specifier = ">=1.2.1" },
65
  { name = "requests", specifier = ">=2.32.5" },
66
  { name = "sentence-transformers", specifier = ">=5.2.0" },
67
+ { name = "sqlmodel", specifier = ">=0.0.37" },
68
  { name = "torch", specifier = ">=2.9.1", index = "https://download.pytorch.org/whl/cpu" },
69
  { name = "torchvision", specifier = ">=0.24.1", index = "https://download.pytorch.org/whl/cpu" },
70
  { name = "uvicorn", specifier = ">=0.38.0" },
 
952
  { url = "https://files.pythonhosted.org/packages/a7/a2/a3497afea984202f481de3464b3bfbcb3de1cd83cbbf3714933d40dd7106/gradio_client-2.0.2-py3-none-any.whl", hash = "sha256:46a7f63eaa7758fe2e38be7f78f26a1fff48a7b526ebdd87141b050e08556622", size = 55566, upload-time = "2025-12-19T18:29:47.508Z" },
953
  ]
954
 
955
+ [[package]]
956
+ name = "greenlet"
957
+ version = "3.3.2"
958
+ source = { registry = "https://pypi.org/simple" }
959
+ sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" }
960
+ wheels = [
961
+ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" },
962
+ { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" },
963
+ { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" },
964
+ { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" },
965
+ { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" },
966
+ { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" },
967
+ { url = "https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5", size = 230389, upload-time = "2026-02-20T20:17:18.772Z" },
968
+ { url = "https://files.pythonhosted.org/packages/e1/a1/65bbc059a43a7e2143ec4fc1f9e3f673e04f9c7b371a494a101422ac4fd5/greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd", size = 229645, upload-time = "2026-02-20T20:18:18.695Z" },
969
+ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" },
970
+ { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" },
971
+ { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" },
972
+ { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" },
973
+ { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" },
974
+ { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" },
975
+ { url = "https://files.pythonhosted.org/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", size = 231081, upload-time = "2026-02-20T20:17:28.121Z" },
976
+ { url = "https://files.pythonhosted.org/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", size = 230331, upload-time = "2026-02-20T20:17:23.34Z" },
977
+ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" },
978
+ { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" },
979
+ { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" },
980
+ { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" },
981
+ { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" },
982
+ { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" },
983
+ { url = "https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", size = 230961, upload-time = "2026-02-20T20:16:58.461Z" },
984
+ { url = "https://files.pythonhosted.org/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", size = 230221, upload-time = "2026-02-20T20:17:37.152Z" },
985
+ ]
986
+
987
  [[package]]
988
  name = "groovy"
989
  version = "0.1.2"
 
1976
  { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" },
1977
  ]
1978
 
 
 
 
 
 
 
 
 
 
 
 
 
1979
  [[package]]
1980
  name = "nest-asyncio"
1981
  version = "1.6.0"
 
2533
  { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
2534
  ]
2535
 
2536
+ [[package]]
2537
+ name = "psycopg2-binary"
2538
+ version = "2.9.11"
2539
+ source = { registry = "https://pypi.org/simple" }
2540
+ sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/8767aaa597ba424643dc87348c6f1754dd9f48e80fdc1b9f7ca5c3a7c213/psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c", size = 379620, upload-time = "2025-10-10T11:14:48.041Z" }
2541
+ wheels = [
2542
+ { url = "https://files.pythonhosted.org/packages/c7/ae/8d8266f6dd183ab4d48b95b9674034e1b482a3f8619b33a0d86438694577/psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10", size = 3756452, upload-time = "2025-10-10T11:11:11.583Z" },
2543
+ { url = "https://files.pythonhosted.org/packages/4b/34/aa03d327739c1be70e09d01182619aca8ebab5970cd0cfa50dd8b9cec2ac/psycopg2_binary-2.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a", size = 3863957, upload-time = "2025-10-10T11:11:16.932Z" },
2544
+ { url = "https://files.pythonhosted.org/packages/48/89/3fdb5902bdab8868bbedc1c6e6023a4e08112ceac5db97fc2012060e0c9a/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4", size = 4410955, upload-time = "2025-10-10T11:11:21.21Z" },
2545
+ { url = "https://files.pythonhosted.org/packages/ce/24/e18339c407a13c72b336e0d9013fbbbde77b6fd13e853979019a1269519c/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7", size = 4468007, upload-time = "2025-10-10T11:11:24.831Z" },
2546
+ { url = "https://files.pythonhosted.org/packages/91/7e/b8441e831a0f16c159b5381698f9f7f7ed54b77d57bc9c5f99144cc78232/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee", size = 4165012, upload-time = "2025-10-10T11:11:29.51Z" },
2547
+ { url = "https://files.pythonhosted.org/packages/0d/61/4aa89eeb6d751f05178a13da95516c036e27468c5d4d2509bb1e15341c81/psycopg2_binary-2.9.11-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb", size = 3981881, upload-time = "2025-10-30T02:55:07.332Z" },
2548
+ { url = "https://files.pythonhosted.org/packages/76/a1/2f5841cae4c635a9459fe7aca8ed771336e9383b6429e05c01267b0774cf/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f", size = 3650985, upload-time = "2025-10-10T11:11:34.975Z" },
2549
+ { url = "https://files.pythonhosted.org/packages/84/74/4defcac9d002bca5709951b975173c8c2fa968e1a95dc713f61b3a8d3b6a/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94", size = 3296039, upload-time = "2025-10-10T11:11:40.432Z" },
2550
+ { url = "https://files.pythonhosted.org/packages/6d/c2/782a3c64403d8ce35b5c50e1b684412cf94f171dc18111be8c976abd2de1/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f", size = 3043477, upload-time = "2025-10-30T02:55:11.182Z" },
2551
+ { url = "https://files.pythonhosted.org/packages/c8/31/36a1d8e702aa35c38fc117c2b8be3f182613faa25d794b8aeaab948d4c03/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908", size = 3345842, upload-time = "2025-10-10T11:11:45.366Z" },
2552
+ { url = "https://files.pythonhosted.org/packages/6e/b4/a5375cda5b54cb95ee9b836930fea30ae5a8f14aa97da7821722323d979b/psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03", size = 2713894, upload-time = "2025-10-10T11:11:48.775Z" },
2553
+ { url = "https://files.pythonhosted.org/packages/d8/91/f870a02f51be4a65987b45a7de4c2e1897dd0d01051e2b559a38fa634e3e/psycopg2_binary-2.9.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4", size = 3756603, upload-time = "2025-10-10T11:11:52.213Z" },
2554
+ { url = "https://files.pythonhosted.org/packages/27/fa/cae40e06849b6c9a95eb5c04d419942f00d9eaac8d81626107461e268821/psycopg2_binary-2.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc", size = 3864509, upload-time = "2025-10-10T11:11:56.452Z" },
2555
+ { url = "https://files.pythonhosted.org/packages/2d/75/364847b879eb630b3ac8293798e380e441a957c53657995053c5ec39a316/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a", size = 4411159, upload-time = "2025-10-10T11:12:00.49Z" },
2556
+ { url = "https://files.pythonhosted.org/packages/6f/a0/567f7ea38b6e1c62aafd58375665a547c00c608a471620c0edc364733e13/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e", size = 4468234, upload-time = "2025-10-10T11:12:04.892Z" },
2557
+ { url = "https://files.pythonhosted.org/packages/30/da/4e42788fb811bbbfd7b7f045570c062f49e350e1d1f3df056c3fb5763353/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db", size = 4166236, upload-time = "2025-10-10T11:12:11.674Z" },
2558
+ { url = "https://files.pythonhosted.org/packages/3c/94/c1777c355bc560992af848d98216148be5f1be001af06e06fc49cbded578/psycopg2_binary-2.9.11-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757", size = 3983083, upload-time = "2025-10-30T02:55:15.73Z" },
2559
+ { url = "https://files.pythonhosted.org/packages/bd/42/c9a21edf0e3daa7825ed04a4a8588686c6c14904344344a039556d78aa58/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3", size = 3652281, upload-time = "2025-10-10T11:12:17.713Z" },
2560
+ { url = "https://files.pythonhosted.org/packages/12/22/dedfbcfa97917982301496b6b5e5e6c5531d1f35dd2b488b08d1ebc52482/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a", size = 3298010, upload-time = "2025-10-10T11:12:22.671Z" },
2561
+ { url = "https://files.pythonhosted.org/packages/66/ea/d3390e6696276078bd01b2ece417deac954dfdd552d2edc3d03204416c0c/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34", size = 3044641, upload-time = "2025-10-30T02:55:19.929Z" },
2562
+ { url = "https://files.pythonhosted.org/packages/12/9a/0402ded6cbd321da0c0ba7d34dc12b29b14f5764c2fc10750daa38e825fc/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d", size = 3347940, upload-time = "2025-10-10T11:12:26.529Z" },
2563
+ { url = "https://files.pythonhosted.org/packages/b1/d2/99b55e85832ccde77b211738ff3925a5d73ad183c0b37bcbbe5a8ff04978/psycopg2_binary-2.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d", size = 2714147, upload-time = "2025-10-10T11:12:29.535Z" },
2564
+ { url = "https://files.pythonhosted.org/packages/ff/a8/a2709681b3ac11b0b1786def10006b8995125ba268c9a54bea6f5ae8bd3e/psycopg2_binary-2.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c", size = 3756572, upload-time = "2025-10-10T11:12:32.873Z" },
2565
+ { url = "https://files.pythonhosted.org/packages/62/e1/c2b38d256d0dafd32713e9f31982a5b028f4a3651f446be70785f484f472/psycopg2_binary-2.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee", size = 3864529, upload-time = "2025-10-10T11:12:36.791Z" },
2566
+ { url = "https://files.pythonhosted.org/packages/11/32/b2ffe8f3853c181e88f0a157c5fb4e383102238d73c52ac6d93a5c8bffe6/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0", size = 4411242, upload-time = "2025-10-10T11:12:42.388Z" },
2567
+ { url = "https://files.pythonhosted.org/packages/10/04/6ca7477e6160ae258dc96f67c371157776564679aefd247b66f4661501a2/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766", size = 4468258, upload-time = "2025-10-10T11:12:48.654Z" },
2568
+ { url = "https://files.pythonhosted.org/packages/3c/7e/6a1a38f86412df101435809f225d57c1a021307dd0689f7a5e7fe83588b1/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3", size = 4166295, upload-time = "2025-10-10T11:12:52.525Z" },
2569
+ { url = "https://files.pythonhosted.org/packages/f2/7d/c07374c501b45f3579a9eb761cbf2604ddef3d96ad48679112c2c5aa9c25/psycopg2_binary-2.9.11-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f", size = 3983133, upload-time = "2025-10-30T02:55:24.329Z" },
2570
+ { url = "https://files.pythonhosted.org/packages/82/56/993b7104cb8345ad7d4516538ccf8f0d0ac640b1ebd8c754a7b024e76878/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4", size = 3652383, upload-time = "2025-10-10T11:12:56.387Z" },
2571
+ { url = "https://files.pythonhosted.org/packages/2d/ac/eaeb6029362fd8d454a27374d84c6866c82c33bfc24587b4face5a8e43ef/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c", size = 3298168, upload-time = "2025-10-10T11:13:00.403Z" },
2572
+ { url = "https://files.pythonhosted.org/packages/2b/39/50c3facc66bded9ada5cbc0de867499a703dc6bca6be03070b4e3b65da6c/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60", size = 3044712, upload-time = "2025-10-30T02:55:27.975Z" },
2573
+ { url = "https://files.pythonhosted.org/packages/9c/8e/b7de019a1f562f72ada81081a12823d3c1590bedc48d7d2559410a2763fe/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1", size = 3347549, upload-time = "2025-10-10T11:13:03.971Z" },
2574
+ { url = "https://files.pythonhosted.org/packages/80/2d/1bb683f64737bbb1f86c82b7359db1eb2be4e2c0c13b947f80efefa7d3e5/psycopg2_binary-2.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa", size = 2714215, upload-time = "2025-10-10T11:13:07.14Z" },
2575
+ ]
2576
+
2577
  [[package]]
2578
  name = "ptyprocess"
2579
  version = "0.7.0"
 
3598
  { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
3599
  ]
3600
 
3601
+ [[package]]
3602
+ name = "sqlalchemy"
3603
+ version = "2.0.47"
3604
+ source = { registry = "https://pypi.org/simple" }
3605
+ dependencies = [
3606
+ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
3607
+ { name = "typing-extensions" },
3608
+ ]
3609
+ sdist = { url = "https://files.pythonhosted.org/packages/cd/4b/1e00561093fe2cd8eef09d406da003c8a118ff02d6548498c1ae677d68d9/sqlalchemy-2.0.47.tar.gz", hash = "sha256:e3e7feb57b267fe897e492b9721ae46d5c7de6f9e8dee58aacf105dc4e154f3d", size = 9886323, upload-time = "2026-02-24T16:34:27.947Z" }
3610
+ wheels = [
3611
+ { url = "https://files.pythonhosted.org/packages/94/13/886338d3e8ab5ddcfe84d54302c749b1793e16c4bba63d7004e3f7baa8ec/sqlalchemy-2.0.47-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a1dbf0913879c443617d6b64403cf2801c941651db8c60e96d204ed9388d6b0", size = 2157124, upload-time = "2026-02-24T16:43:54.706Z" },
3612
+ { url = "https://files.pythonhosted.org/packages/b6/bb/a897f6a66c9986aa9f27f5cf8550637d8a5ea368fd7fb42f6dac3105b4dc/sqlalchemy-2.0.47-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:775effbb97ea3b00c4dd3aeaf3ba8acba6e3e2b4b41d17d67a27e696843dbc95", size = 3313513, upload-time = "2026-02-24T17:29:00.527Z" },
3613
+ { url = "https://files.pythonhosted.org/packages/59/fb/69bfae022b681507565ab0d34f0c80aa1e9f954a5a7cbfb0ed054966ac8d/sqlalchemy-2.0.47-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56cc834a3ffac34270cc2a41875e0f40e97aa651f4f3ca1cfbbf421c044cb62b", size = 3313014, upload-time = "2026-02-24T17:27:11.679Z" },
3614
+ { url = "https://files.pythonhosted.org/packages/04/f3/0eba329f7c182d53205a228c4fd24651b95489b431ea2bd830887b4c13c4/sqlalchemy-2.0.47-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49b5e0c7244262f39e767c018e4fdb5e5dbc23cd54c5ddac8eea8f0ba32ef890", size = 3265389, upload-time = "2026-02-24T17:29:02.497Z" },
3615
+ { url = "https://files.pythonhosted.org/packages/5c/06/654edc084b3b46ac79e04200d7c46467ae80c759c4ee41c897f9272b036f/sqlalchemy-2.0.47-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cd822a3f1f6f77b5b841a30c1a07a07f7dee3385f17e638e1722de9ab683be", size = 3287604, upload-time = "2026-02-24T17:27:13.295Z" },
3616
+ { url = "https://files.pythonhosted.org/packages/78/33/c18c8f63b61981219d3aa12321bb7ccee605034d195e868ed94f9727b27c/sqlalchemy-2.0.47-cp311-cp311-win32.whl", hash = "sha256:9847a19548cd283a65e1ce0afd54016598d55ff72682d6fd3e493af6fc044064", size = 2116916, upload-time = "2026-02-24T17:14:37.392Z" },
3617
+ { url = "https://files.pythonhosted.org/packages/f5/c6/a59e3f9796fff844e16afbd821db9abfd6e12698db9441a231a96193a100/sqlalchemy-2.0.47-cp311-cp311-win_amd64.whl", hash = "sha256:722abf1c82aeca46a1a0803711244a48a298279eeaec9e02f7bfee9e064182e5", size = 2141587, upload-time = "2026-02-24T17:14:39.746Z" },
3618
+ { url = "https://files.pythonhosted.org/packages/80/88/74eb470223ff88ea6572a132c0b8de8c1d8ed7b843d3b44a8a3c77f31d39/sqlalchemy-2.0.47-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fa91b19d6b9821c04cc8f7aa2476429cc8887b9687c762815aa629f5c0edec1", size = 2155687, upload-time = "2026-02-24T17:05:46.451Z" },
3619
+ { url = "https://files.pythonhosted.org/packages/ef/ba/1447d3d558971b036cb93b557595cb5dcdfe728f1c7ac4dec16505ef5756/sqlalchemy-2.0.47-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c5bbbd14eff577c8c79cbfe39a0771eecd20f430f3678533476f0087138f356", size = 3336978, upload-time = "2026-02-24T17:18:04.597Z" },
3620
+ { url = "https://files.pythonhosted.org/packages/8a/07/b47472d2ffd0776826f17ccf0b4d01b224c99fbd1904aeb103dffbb4b1cc/sqlalchemy-2.0.47-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a6c555da8d4280a3c4c78c5b7a3f990cee2b2884e5f934f87a226191682ff7", size = 3349939, upload-time = "2026-02-24T17:27:18.937Z" },
3621
+ { url = "https://files.pythonhosted.org/packages/bb/c6/95fa32b79b57769da3e16f054cf658d90940317b5ca0ec20eac84aa19c4f/sqlalchemy-2.0.47-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ed48a1701d24dff3bb49a5bce94d6bc84cbe33d98af2aa2d3cdcce3dea1709ec", size = 3279648, upload-time = "2026-02-24T17:18:07.038Z" },
3622
+ { url = "https://files.pythonhosted.org/packages/bb/c8/3d07e7c73928dc59a0bed40961ca4e313e797bce650b088e8d5fdd3ad939/sqlalchemy-2.0.47-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f3178c920ad98158f0b6309382194df04b14808fa6052ae07099fdde29d5602", size = 3314695, upload-time = "2026-02-24T17:27:20.93Z" },
3623
+ { url = "https://files.pythonhosted.org/packages/6b/d2/ed32b1611c1e19fdb028eee1adc5a9aa138c2952d09ae11f1670170f80ae/sqlalchemy-2.0.47-cp312-cp312-win32.whl", hash = "sha256:b9c11ac9934dd59ece9619fe42780a08abe2faab7b0543bb00d5eabea4f421b9", size = 2115502, upload-time = "2026-02-24T17:22:52.546Z" },
3624
+ { url = "https://files.pythonhosted.org/packages/fd/52/9de590356a4dd8e9ef5a881dbba64b2bbc4cbc71bf02bc68e775fb9b1899/sqlalchemy-2.0.47-cp312-cp312-win_amd64.whl", hash = "sha256:db43b72cf8274a99e089755c9c1e0b947159b71adbc2c83c3de2e38d5d607acb", size = 2142435, upload-time = "2026-02-24T17:22:54.268Z" },
3625
+ { url = "https://files.pythonhosted.org/packages/4a/e5/0af64ce7d8f60ec5328c10084e2f449e7912a9b8bdbefdcfb44454a25f49/sqlalchemy-2.0.47-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:456a135b790da5d3c6b53d0ef71ac7b7d280b7f41eb0c438986352bf03ca7143", size = 2152551, upload-time = "2026-02-24T17:05:47.675Z" },
3626
+ { url = "https://files.pythonhosted.org/packages/63/79/746b8d15f6940e2ac469ce22d7aa5b1124b1ab820bad9b046eb3000c88a6/sqlalchemy-2.0.47-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09a2f7698e44b3135433387da5d8846cf7cc7c10e5425af7c05fee609df978b6", size = 3278782, upload-time = "2026-02-24T17:18:10.012Z" },
3627
+ { url = "https://files.pythonhosted.org/packages/91/b1/bd793ddb34345d1ed43b13ab2d88c95d7d4eb2e28f5b5a99128b9cc2bca2/sqlalchemy-2.0.47-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bbc72e6a177c78d724f9106aaddc0d26a2ada89c6332b5935414eccf04cbd5", size = 3295155, upload-time = "2026-02-24T17:27:22.827Z" },
3628
+ { url = "https://files.pythonhosted.org/packages/97/84/7213def33f94e5ca6f5718d259bc9f29de0363134648425aa218d4356b23/sqlalchemy-2.0.47-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:75460456b043b78b6006e41bdf5b86747ee42eafaf7fffa3b24a6e9a456a2092", size = 3226834, upload-time = "2026-02-24T17:18:11.465Z" },
3629
+ { url = "https://files.pythonhosted.org/packages/ef/06/456810204f4dc29b5f025b1b0a03b4bd6b600ebf3c1040aebd90a257fa33/sqlalchemy-2.0.47-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d9adaa616c3bc7d80f9ded57cd84b51d6617cad6a5456621d858c9f23aaee01", size = 3265001, upload-time = "2026-02-24T17:27:24.813Z" },
3630
+ { url = "https://files.pythonhosted.org/packages/fb/20/df3920a4b2217dbd7390a5bd277c1902e0393f42baaf49f49b3c935e7328/sqlalchemy-2.0.47-cp313-cp313-win32.whl", hash = "sha256:76e09f974382a496a5ed985db9343628b1cb1ac911f27342e4cc46a8bac10476", size = 2113647, upload-time = "2026-02-24T17:22:55.747Z" },
3631
+ { url = "https://files.pythonhosted.org/packages/46/06/7873ddf69918efbfabd7211829f4bd8019739d0a719253112d305d3ba51d/sqlalchemy-2.0.47-cp313-cp313-win_amd64.whl", hash = "sha256:0664089b0bf6724a0bfb49a0cf4d4da24868a0a5c8e937cd7db356d5dcdf2c66", size = 2139425, upload-time = "2026-02-24T17:22:57.033Z" },
3632
+ { url = "https://files.pythonhosted.org/packages/54/fa/61ad9731370c90ac7ea5bf8f5eaa12c48bb4beec41c0fa0360becf4ac10d/sqlalchemy-2.0.47-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed0c967c701ae13da98eb220f9ddab3044ab63504c1ba24ad6a59b26826ad003", size = 3558809, upload-time = "2026-02-24T17:12:15.232Z" },
3633
+ { url = "https://files.pythonhosted.org/packages/33/d5/221fac96f0529391fe374875633804c866f2b21a9c6d3a6ca57d9c12cfd7/sqlalchemy-2.0.47-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3537943a61fd25b241e976426a0c6814434b93cf9b09d39e8e78f3c9eb9a487", size = 3525480, upload-time = "2026-02-24T17:27:59.602Z" },
3634
+ { url = "https://files.pythonhosted.org/packages/ec/55/8247d53998c3673e4a8d1958eba75c6f5cc3b39082029d400bb1f2a911ae/sqlalchemy-2.0.47-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:57f7e336a64a0dba686c66392d46b9bc7af2c57d55ce6dc1697b4ef32b043ceb", size = 3466569, upload-time = "2026-02-24T17:12:16.94Z" },
3635
+ { url = "https://files.pythonhosted.org/packages/6b/b5/c1f0eea1bac6790845f71420a7fe2f2a0566203aa57543117d4af3b77d1c/sqlalchemy-2.0.47-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dff735a621858680217cb5142b779bad40ef7322ddbb7c12062190db6879772e", size = 3475770, upload-time = "2026-02-24T17:28:02.034Z" },
3636
+ { url = "https://files.pythonhosted.org/packages/c5/ed/2f43f92474ea0c43c204657dc47d9d002cd738b96ca2af8e6d29a9b5e42d/sqlalchemy-2.0.47-cp313-cp313t-win32.whl", hash = "sha256:3893dc096bb3cca9608ea3487372ffcea3ae9b162f40e4d3c51dd49db1d1b2dc", size = 2141300, upload-time = "2026-02-24T17:14:37.024Z" },
3637
+ { url = "https://files.pythonhosted.org/packages/cc/a9/8b73f9f1695b6e92f7aaf1711135a1e3bbeb78bca9eded35cb79180d3c6d/sqlalchemy-2.0.47-cp313-cp313t-win_amd64.whl", hash = "sha256:b5103427466f4b3e61f04833ae01f9a914b1280a2a8bcde3a9d7ab11f3755b42", size = 2173053, upload-time = "2026-02-24T17:14:38.688Z" },
3638
+ { url = "https://files.pythonhosted.org/packages/15/9f/7c378406b592fcf1fc157248607b495a40e3202ba4a6f1372a2ba6447717/sqlalchemy-2.0.47-py3-none-any.whl", hash = "sha256:e2647043599297a1ef10e720cf310846b7f31b6c841fee093d2b09d81215eb93", size = 1940159, upload-time = "2026-02-24T17:15:07.158Z" },
3639
+ ]
3640
+
3641
+ [[package]]
3642
+ name = "sqlmodel"
3643
+ version = "0.0.37"
3644
+ source = { registry = "https://pypi.org/simple" }
3645
+ dependencies = [
3646
+ { name = "pydantic" },
3647
+ { name = "sqlalchemy" },
3648
+ ]
3649
+ sdist = { url = "https://files.pythonhosted.org/packages/fb/26/1d2faa0fd5a765267f49751de533adac6b9ff9366c7c6e7692df4f32230f/sqlmodel-0.0.37.tar.gz", hash = "sha256:d2c19327175794faf50b1ee31cc966764f55b1dedefc046450bc5741a3d68352", size = 85527, upload-time = "2026-02-21T16:39:47.038Z" }
3650
+ wheels = [
3651
+ { url = "https://files.pythonhosted.org/packages/b1/e1/7c8d18e737433f3b5bbe27b56a9072a9fcb36342b48f1bef34b6da1d61f2/sqlmodel-0.0.37-py3-none-any.whl", hash = "sha256:2137a4045ef3fd66a917a7717ada959a1ceb3630d95e1f6aaab39dd2c0aef278", size = 27224, upload-time = "2026-02-21T16:39:47.781Z" },
3652
+ ]
3653
+
3654
  [[package]]
3655
  name = "stack-data"
3656
  version = "0.6.3"
 
3787
  { name = "typing-extensions", marker = "sys_platform == 'darwin'" },
3788
  ]
3789
  wheels = [
3790
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:a52952a8c90a422c14627ea99b9826b7557203b46b4d0772d3ca5c7699692425" },
3791
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:287242dd1f830846098b5eca847f817aa5c6015ea57ab4c1287809efea7b77eb" },
3792
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8924d10d36eac8fe0652a060a03fc2ae52980841850b9a1a2ddb0f27a4f181cd" },
3793
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:bcee64ae7aa65876ceeae6dcaebe75109485b213528c74939602208a20706e3f" },
3794
  ]
3795
 
3796
  [[package]]
 
3815
  { name = "typing-extensions", marker = "sys_platform != 'darwin'" },
3816
  ]
3817
  wheels = [
3818
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0e611cfb16724e62252b67d31073bc5c490cb83e92ecdc1192762535e0e44487" },
3819
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3de2adb9b4443dc9210ef1f1b16da3647ace53553166d6360bbbd7edd6f16e4d" },
3820
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-win_amd64.whl", hash = "sha256:69b3785d28be5a9c56ab525788ec5000349ec59132a74b7d5e954b905015b992" },
3821
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp311-cp311-win_arm64.whl", hash = "sha256:15b4ae6fe371d96bffb8e1e9af62164797db20a0dc1337345781659cfd0b8bb1" },
3822
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3bf9b442a51a2948e41216a76d7ab00f0694cfcaaa51b6f9bcab57b7f89843e6" },
3823
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7417d8c565f219d3455654cb431c6d892a3eb40246055e14d645422de13b9ea1" },
3824
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:a4e06b4f441675d26b462123c8a83e77c55f1ec8ebc081203be2db1ea8054add" },
3825
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:1abe31f14b560c1f062699e966cb08ef5b67518a1cfac2d8547a3dbcd8387b06" },
3826
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3e532e553b37ee859205a9b2d1c7977fd6922f53bbb1b9bfdd5bdc00d1a60ed4" },
3827
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:39b3dff6d8fba240ae0d1bede4ca11c2531ae3b47329206512d99e17907ff74b" },
3828
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:404a7ab2fffaf2ca069e662f331eb46313692b2f1630df2720094284f390ccef" },
3829
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:161decbff26a33f13cb5ba6d2c8f458bbf56193bcc32ecc70be6dd4c7a3ee79d" },
3830
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:01b1884f724977a20c7da2f640f1c7b37f4a2c117a7f4a6c1c0424d14cb86322" },
3831
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:031a597147fa81b1e6d79ccf1ad3ccc7fafa27941d6cf26ff5caaa384fb20e92" },
3832
+ { url = "https://download.pytorch.org/whl/cpu/torch-2.9.1%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:e586ab1363e3f86aa4cc133b7fdcf98deb1d2c13d43a7a6e5a6a18e9c5364893" },
3833
  ]
3834
 
3835
  [[package]]