Spaces:
Running
Running
Upload studio_generate.py
Browse files- studio_generate.py +29 -20
studio_generate.py
CHANGED
|
@@ -54,70 +54,79 @@ POLAR_ORG_ID = os.environ.get("POLAR_ORG_ID", "")
|
|
| 54 |
FIREBASE_KEY_PATH = os.environ.get("FIREBASE_KEY_PATH", "serviceAccountKey.json")
|
| 55 |
ADMIN_UIDS = set(os.environ.get("STUDIO_ADMIN_UIDS", "").split(","))
|
| 56 |
|
| 57 |
-
# ββ MONGODB (
|
| 58 |
-
# IMPORTANT: We use pymongo (sync) β NOT motor (async). Motor creates a separate
|
| 59 |
-
# connection that fails on Render. Instead we reuse main.py's existing working
|
| 60 |
-
# MongoClient via sys.modules, or create our own with the same SSL settings.
|
| 61 |
import sys as _sys
|
| 62 |
|
|
|
|
|
|
|
| 63 |
def _get_db():
|
| 64 |
-
"""Get the neuraprompt database.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
main_mod = _sys.modules.get("main")
|
| 66 |
if main_mod and hasattr(main_mod, "neuraprompt_db"):
|
| 67 |
-
|
|
|
|
|
|
|
| 68 |
# Fallback: create our own pymongo client (same SSL settings as main.py)
|
|
|
|
| 69 |
_client = MongoClient(
|
| 70 |
MONGO_URL, ssl=True,
|
| 71 |
tlsAllowInvalidCertificates=False,
|
| 72 |
tlsCAFile="/etc/ssl/certs/ca-certificates.crt",
|
| 73 |
server_api=ServerApi("1"),
|
| 74 |
)
|
| 75 |
-
|
|
|
|
| 76 |
|
| 77 |
-
db
|
| 78 |
-
|
| 79 |
|
| 80 |
# ββ ASYNC DB HELPER (pymongo is sync, so we wrap calls in asyncio.to_thread) ββ
|
| 81 |
async def _db_find_one(collection, query, projection=None):
|
| 82 |
"""Async wrapper for db.collection.find_one()"""
|
| 83 |
return await asyncio.to_thread(
|
| 84 |
-
lambda:
|
| 85 |
)
|
| 86 |
|
| 87 |
async def _db_update_one(collection, query, update, upsert=False):
|
| 88 |
-
return await asyncio.to_thread(lambda:
|
| 89 |
|
| 90 |
async def _db_insert_one(collection, doc):
|
| 91 |
-
return await asyncio.to_thread(lambda:
|
| 92 |
|
| 93 |
async def _db_count_documents(collection, query):
|
| 94 |
-
return await asyncio.to_thread(lambda:
|
| 95 |
|
| 96 |
async def _db_find(collection, query, projection=None, sort=None, limit=None):
|
| 97 |
def _do():
|
| 98 |
-
cursor =
|
| 99 |
if sort: cursor = cursor.sort(*sort) if isinstance(sort, tuple) else cursor.sort(sort)
|
| 100 |
if limit: cursor = cursor.limit(limit)
|
| 101 |
return list(cursor)
|
| 102 |
return await asyncio.to_thread(_do)
|
| 103 |
|
| 104 |
async def _db_delete_one(collection, query):
|
| 105 |
-
return await asyncio.to_thread(lambda:
|
| 106 |
|
| 107 |
async def _db_delete_many(collection, query):
|
| 108 |
-
return await asyncio.to_thread(lambda:
|
| 109 |
|
| 110 |
async def _db_insert_many(collection, docs):
|
| 111 |
-
return await asyncio.to_thread(lambda:
|
| 112 |
|
| 113 |
async def _db_distinct(collection, field, query=None):
|
| 114 |
-
return await asyncio.to_thread(lambda:
|
| 115 |
|
| 116 |
async def _db_aggregate(collection, pipeline):
|
| 117 |
-
return await asyncio.to_thread(lambda: list(
|
| 118 |
|
| 119 |
async def _db_create_index(collection, keys, **kwargs):
|
| 120 |
-
return await asyncio.to_thread(lambda:
|
| 121 |
|
| 122 |
|
| 123 |
# ββ CREDIT SYSTEM βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 54 |
FIREBASE_KEY_PATH = os.environ.get("FIREBASE_KEY_PATH", "serviceAccountKey.json")
|
| 55 |
ADMIN_UIDS = set(os.environ.get("STUDIO_ADMIN_UIDS", "").split(","))
|
| 56 |
|
| 57 |
+
# ββ MONGODB (lazy β waits until main.py is loaded, then reuses its connection) β
|
|
|
|
|
|
|
|
|
|
| 58 |
import sys as _sys
|
| 59 |
|
| 60 |
+
_db_instance = None
|
| 61 |
+
|
| 62 |
def _get_db():
|
| 63 |
+
"""Get the neuraprompt database. LAZY: reuses main.py's connection.
|
| 64 |
+
Called on first DB operation, not at import time β this ensures main.py
|
| 65 |
+
has finished initializing its MongoClient before we try to reuse it."""
|
| 66 |
+
global _db_instance
|
| 67 |
+
if _db_instance is not None:
|
| 68 |
+
return _db_instance
|
| 69 |
+
# Try to reuse main.py's connection (it's already connected to Atlas)
|
| 70 |
main_mod = _sys.modules.get("main")
|
| 71 |
if main_mod and hasattr(main_mod, "neuraprompt_db"):
|
| 72 |
+
_db_instance = main_mod.neuraprompt_db
|
| 73 |
+
print("[Studio v2] MongoDB: reusing main.py connection")
|
| 74 |
+
return _db_instance
|
| 75 |
# Fallback: create our own pymongo client (same SSL settings as main.py)
|
| 76 |
+
print("[Studio v2] MongoDB: main.py not available, creating own connection")
|
| 77 |
_client = MongoClient(
|
| 78 |
MONGO_URL, ssl=True,
|
| 79 |
tlsAllowInvalidCertificates=False,
|
| 80 |
tlsCAFile="/etc/ssl/certs/ca-certificates.crt",
|
| 81 |
server_api=ServerApi("1"),
|
| 82 |
)
|
| 83 |
+
_db_instance = _client["neuraprompt"]
|
| 84 |
+
return _db_instance
|
| 85 |
|
| 86 |
+
# NOTE: db is NOT set at import time β it's resolved lazily on first use.
|
| 87 |
+
# All _db_* helpers call _get_db() internally.
|
| 88 |
|
| 89 |
# ββ ASYNC DB HELPER (pymongo is sync, so we wrap calls in asyncio.to_thread) ββ
|
| 90 |
async def _db_find_one(collection, query, projection=None):
|
| 91 |
"""Async wrapper for db.collection.find_one()"""
|
| 92 |
return await asyncio.to_thread(
|
| 93 |
+
lambda: _get_db()[collection].find_one(query, projection) if projection else db[collection].find_one(query)
|
| 94 |
)
|
| 95 |
|
| 96 |
async def _db_update_one(collection, query, update, upsert=False):
|
| 97 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].update_one(query, update, upsert=upsert))
|
| 98 |
|
| 99 |
async def _db_insert_one(collection, doc):
|
| 100 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].insert_one(doc))
|
| 101 |
|
| 102 |
async def _db_count_documents(collection, query):
|
| 103 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].count_documents(query))
|
| 104 |
|
| 105 |
async def _db_find(collection, query, projection=None, sort=None, limit=None):
|
| 106 |
def _do():
|
| 107 |
+
cursor = _get_db()[collection].find(query, projection) if projection else db[collection].find(query)
|
| 108 |
if sort: cursor = cursor.sort(*sort) if isinstance(sort, tuple) else cursor.sort(sort)
|
| 109 |
if limit: cursor = cursor.limit(limit)
|
| 110 |
return list(cursor)
|
| 111 |
return await asyncio.to_thread(_do)
|
| 112 |
|
| 113 |
async def _db_delete_one(collection, query):
|
| 114 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].delete_one(query))
|
| 115 |
|
| 116 |
async def _db_delete_many(collection, query):
|
| 117 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].delete_many(query))
|
| 118 |
|
| 119 |
async def _db_insert_many(collection, docs):
|
| 120 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].insert_many(docs))
|
| 121 |
|
| 122 |
async def _db_distinct(collection, field, query=None):
|
| 123 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].distinct(field, query or {}))
|
| 124 |
|
| 125 |
async def _db_aggregate(collection, pipeline):
|
| 126 |
+
return await asyncio.to_thread(lambda: list(_get_db()[collection].aggregate(pipeline)))
|
| 127 |
|
| 128 |
async def _db_create_index(collection, keys, **kwargs):
|
| 129 |
+
return await asyncio.to_thread(lambda: _get_db()[collection].create_index(keys, **kwargs))
|
| 130 |
|
| 131 |
|
| 132 |
# ββ CREDIT SYSTEM βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|