From 1b14d6c8e5812c480da8d40830c9a092301f88df Mon Sep 17 00:00:00 2001 From: phact Date: Tue, 2 Sep 2025 10:50:20 -0400 Subject: [PATCH] no auth mode --- frontend/src/app/settings/page.tsx | 125 +++++++++++++-------------- src/api/chat.py | 8 +- src/api/connectors.py | 2 +- src/api/knowledge_filter.py | 16 ++-- src/api/search.py | 6 +- src/api/upload.py | 10 +-- src/auth_middleware.py | 48 ++++++++++- src/config/settings.py | 6 ++ src/main.py | 17 ++-- src/services/auth_service.py | 17 +++- src/services/document_service.py | 22 +++-- src/services/search_service.py | 9 +- src/session_manager.py | 30 ++++++- uv.lock | 130 +++++++++++++++++++++-------- 14 files changed, 303 insertions(+), 143 deletions(-) diff --git a/frontend/src/app/settings/page.tsx b/frontend/src/app/settings/page.tsx index 05220d4c..c42cbeb8 100644 --- a/frontend/src/app/settings/page.tsx +++ b/frontend/src/app/settings/page.tsx @@ -41,7 +41,7 @@ interface Connection { } function KnowledgeSourcesPage() { - const { isAuthenticated } = useAuth() + const { isAuthenticated, isNoAuthMode } = useAuth() const { addTask, tasks } = useTask() const searchParams = useSearchParams() @@ -351,48 +351,66 @@ function KnowledgeSourcesPage() {

Cloud Connectors

- {/* Sync Settings */} -
-
-

Sync Settings

-

Configure how many files to sync when manually triggering a sync

-
-
-
- { - setSyncAllFiles(!!checked) - if (checked) { - setMaxFiles(0) - } else { - setMaxFiles(10) - } - }} - /> -
+ )} {/* Connectors Grid */}
@@ -468,33 +486,6 @@ function KnowledgeSourcesPage() { ))}
- {/* Coming Soon Section */} - - - Coming Soon - - Additional connectors are in development - - - -
-
-
D
-
-
Dropbox
-
File storage
-
-
-
-
B
-
-
Box
-
Enterprise file sharing
-
-
-
-
-
) diff --git a/src/api/chat.py b/src/api/chat.py index 112bfbf8..98e16e00 100644 --- a/src/api/chat.py +++ b/src/api/chat.py @@ -14,8 +14,8 @@ async def chat_endpoint(request: Request, chat_service, session_manager): user = request.state.user user_id = user.user_id - # Get JWT token from request cookie - jwt_token = request.cookies.get("auth_token") + # Get JWT token from auth middleware + jwt_token = request.state.jwt_token if not prompt: return JSONResponse({"error": "Prompt is required"}, status_code=400) @@ -57,8 +57,8 @@ async def langflow_endpoint(request: Request, chat_service, session_manager): user = request.state.user user_id = user.user_id - # Get JWT token from request cookie - jwt_token = request.cookies.get("auth_token") + # Get JWT token from auth middleware + jwt_token = request.state.jwt_token if not prompt: return JSONResponse({"error": "Prompt is required"}, status_code=400) diff --git a/src/api/connectors.py b/src/api/connectors.py index b548e88e..1360cca5 100644 --- a/src/api/connectors.py +++ b/src/api/connectors.py @@ -20,7 +20,7 @@ async def connector_sync(request: Request, connector_service, session_manager): print(f"[DEBUG] Starting connector sync for connector_type={connector_type}, max_files={max_files}") user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token print(f"[DEBUG] User: {user.user_id}") # Get all active connections for this connector type and user diff --git a/src/api/knowledge_filter.py b/src/api/knowledge_filter.py index e004ff3d..1e2f013c 100644 --- a/src/api/knowledge_filter.py +++ b/src/api/knowledge_filter.py @@ -18,7 +18,7 @@ async def create_knowledge_filter(request: Request, knowledge_filter_service, se return JSONResponse({"error": "Query data is required"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token # Create knowledge filter document filter_id = str(uuid.uuid4()) @@ -54,7 +54,7 @@ async def search_knowledge_filters(request: Request, knowledge_filter_service, s limit = payload.get("limit", 20) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token result = await knowledge_filter_service.search_knowledge_filters(query, user_id=user.user_id, jwt_token=jwt_token, limit=limit) @@ -75,7 +75,7 @@ async def get_knowledge_filter(request: Request, knowledge_filter_service, sessi return JSONResponse({"error": "Knowledge filter ID is required"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token result = await knowledge_filter_service.get_knowledge_filter(filter_id, user_id=user.user_id, jwt_token=jwt_token) @@ -100,7 +100,7 @@ async def update_knowledge_filter(request: Request, knowledge_filter_service, se payload = await request.json() user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token # First, get the existing knowledge filter existing_result = await knowledge_filter_service.get_knowledge_filter(filter_id, user_id=user.user_id, jwt_token=jwt_token) @@ -147,7 +147,7 @@ async def delete_knowledge_filter(request: Request, knowledge_filter_service, se return JSONResponse({"error": "Knowledge filter ID is required"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token result = await knowledge_filter_service.delete_knowledge_filter(filter_id, user_id=user.user_id, jwt_token=jwt_token) @@ -171,7 +171,7 @@ async def subscribe_to_knowledge_filter(request: Request, knowledge_filter_servi payload = await request.json() user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token # Get the knowledge filter to validate it exists and get its details filter_result = await knowledge_filter_service.get_knowledge_filter(filter_id, user_id=user.user_id, jwt_token=jwt_token) @@ -227,7 +227,7 @@ async def list_knowledge_filter_subscriptions(request: Request, knowledge_filter return JSONResponse({"error": "Knowledge filter ID is required"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token result = await knowledge_filter_service.get_filter_subscriptions(filter_id, user_id=user.user_id, jwt_token=jwt_token) @@ -251,7 +251,7 @@ async def cancel_knowledge_filter_subscription(request: Request, knowledge_filte return JSONResponse({"error": "Knowledge filter ID and subscription ID are required"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token # Get subscription details to find the monitor ID subscriptions_result = await knowledge_filter_service.get_filter_subscriptions(filter_id, user_id=user.user_id, jwt_token=jwt_token) diff --git a/src/api/search.py b/src/api/search.py index b97adecd..5a138b7d 100644 --- a/src/api/search.py +++ b/src/api/search.py @@ -14,8 +14,10 @@ async def search(request: Request, search_service, session_manager): score_threshold = payload.get("scoreThreshold", 0) # Optional score threshold, defaults to 0 user = request.state.user - # Extract JWT token from cookie for OpenSearch OIDC auth - jwt_token = request.cookies.get("auth_token") + # Extract JWT token from auth middleware + jwt_token = request.state.jwt_token + + print(f"[DEBUG] search API: user={user}, user_id={user.user_id if user else None}, jwt_token={'None' if jwt_token is None else 'present'}") result = await search_service.search(query, user_id=user.user_id, jwt_token=jwt_token, filters=filters, limit=limit, score_threshold=score_threshold) return JSONResponse(result, status_code=200) diff --git a/src/api/upload.py b/src/api/upload.py index d55081fb..7aef5813 100644 --- a/src/api/upload.py +++ b/src/api/upload.py @@ -10,7 +10,7 @@ async def upload(request: Request, document_service, session_manager): form = await request.form() upload_file = form["file"] user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token result = await document_service.process_upload_file(upload_file, owner_user_id=user.user_id, jwt_token=jwt_token, owner_name=user.name, owner_email=user.email) return JSONResponse(result, status_code=201) # Created @@ -36,7 +36,7 @@ async def upload_path(request: Request, task_service, session_manager): return JSONResponse({"error": "No files found in directory"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token task_id = await task_service.create_upload_task(user.user_id, file_paths, jwt_token=jwt_token, owner_name=user.name, owner_email=user.email) return JSONResponse({ @@ -55,8 +55,8 @@ async def upload_context(request: Request, document_service, chat_service, sessi previous_response_id = form.get("previous_response_id") endpoint = form.get("endpoint", "langflow") - # Get JWT token from request cookie for authentication - jwt_token = request.cookies.get("auth_token") + # Get JWT token from auth middleware + jwt_token = request.state.jwt_token # Get user info from request state (set by auth middleware) user = request.state.user @@ -122,7 +122,7 @@ async def upload_bucket(request: Request, task_service, session_manager): return JSONResponse({"error": "No files found in bucket"}, status_code=400) user = request.state.user - jwt_token = request.cookies.get("auth_token") + jwt_token = request.state.jwt_token from models.processors import S3FileProcessor diff --git a/src/auth_middleware.py b/src/auth_middleware.py index f0e12bfd..d3a02625 100644 --- a/src/auth_middleware.py +++ b/src/auth_middleware.py @@ -2,10 +2,15 @@ from starlette.requests import Request from starlette.responses import JSONResponse from typing import Optional from session_manager import User +from config.settings import is_no_auth_mode def get_current_user(request: Request, session_manager) -> Optional[User]: """Extract current user from request cookies""" + # In no-auth mode, ignore cookies entirely + if is_no_auth_mode(): + return None + auth_token = request.cookies.get("auth_token") if not auth_token: return None @@ -17,6 +22,25 @@ def require_auth(session_manager): """Decorator to require authentication for endpoints""" def decorator(handler): async def wrapper(request: Request): + # In no-auth mode, bypass authentication entirely + if is_no_auth_mode(): + print(f"[DEBUG] No-auth mode: Creating anonymous user") + # Create an anonymous user object so endpoints don't break + from session_manager import User + from datetime import datetime + request.state.user = User( + user_id="anonymous", + email="anonymous@localhost", + name="Anonymous User", + picture=None, + provider="none", + created_at=datetime.now(), + last_login=datetime.now() + ) + request.state.jwt_token = None # No JWT in no-auth mode + print(f"[DEBUG] Set user_id=anonymous, jwt_token=None") + return await handler(request) + user = get_current_user(request, session_manager) if not user: return JSONResponse( @@ -24,8 +48,9 @@ def require_auth(session_manager): status_code=401 ) - # Add user to request state so handlers can access it + # Add user and JWT token to request state so handlers can access them request.state.user = user + request.state.jwt_token = None if is_no_auth_mode() else request.cookies.get("auth_token") return await handler(request) return wrapper @@ -36,8 +61,25 @@ def optional_auth(session_manager): """Decorator to optionally extract user for endpoints""" def decorator(handler): async def wrapper(request: Request): - user = get_current_user(request, session_manager) - request.state.user = user # Can be None + # In no-auth mode, create anonymous user + if is_no_auth_mode(): + # Create an anonymous user object so endpoints don't break + from session_manager import User + from datetime import datetime + request.state.user = User( + user_id="anonymous", + email="anonymous@localhost", + name="Anonymous User", + picture=None, + provider="none", + created_at=datetime.now(), + last_login=datetime.now() + ) + request.state.jwt_token = None # No JWT in no-auth mode + else: + user = get_current_user(request, session_manager) + request.state.user = user # Can be None + request.state.jwt_token = None if is_no_auth_mode() else (request.cookies.get("auth_token") if user else None) return await handler(request) return wrapper diff --git a/src/config/settings.py b/src/config/settings.py index 5a8850c8..3a42fa1c 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -30,6 +30,12 @@ SESSION_SECRET = os.getenv("SESSION_SECRET", "your-secret-key-change-in-producti GOOGLE_OAUTH_CLIENT_ID = os.getenv("GOOGLE_OAUTH_CLIENT_ID") GOOGLE_OAUTH_CLIENT_SECRET = os.getenv("GOOGLE_OAUTH_CLIENT_SECRET") +def is_no_auth_mode(): + """Check if we're running in no-auth mode (OAuth credentials missing)""" + result = not (GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET) + print(f"[DEBUG] is_no_auth_mode() = {result}, CLIENT_ID={GOOGLE_OAUTH_CLIENT_ID is not None}, CLIENT_SECRET={GOOGLE_OAUTH_CLIENT_SECRET is not None}") + return result + # Webhook configuration - must be set to enable webhooks WEBHOOK_BASE_URL = os.getenv("WEBHOOK_BASE_URL") # No default - must be explicitly configured diff --git a/src/main.py b/src/main.py index 32cc31a9..5f0aeedc 100644 --- a/src/main.py +++ b/src/main.py @@ -192,12 +192,17 @@ async def initialize_services(): # Load persisted connector connections at startup so webhooks and syncs # can resolve existing subscriptions immediately after server boot - try: - await connector_service.initialize() - loaded_count = len(connector_service.connection_manager.connections) - print(f"[CONNECTORS] Loaded {loaded_count} persisted connection(s) on startup") - except Exception as e: - print(f"[WARNING] Failed to load persisted connections on startup: {e}") + # Skip in no-auth mode since connectors require OAuth + from config.settings import is_no_auth_mode + if not is_no_auth_mode(): + try: + await connector_service.initialize() + loaded_count = len(connector_service.connection_manager.connections) + print(f"[CONNECTORS] Loaded {loaded_count} persisted connection(s) on startup") + except Exception as e: + print(f"[WARNING] Failed to load persisted connections on startup: {e}") + else: + print(f"[CONNECTORS] Skipping connection loading in no-auth mode") return { 'document_service': document_service, diff --git a/src/services/auth_service.py b/src/services/auth_service.py index 70c1d8b7..52093b00 100644 --- a/src/services/auth_service.py +++ b/src/services/auth_service.py @@ -6,7 +6,7 @@ import aiofiles from datetime import datetime, timedelta from typing import Optional -from config.settings import WEBHOOK_BASE_URL +from config.settings import WEBHOOK_BASE_URL, is_no_auth_mode from session_manager import SessionManager from connectors.google_drive.oauth import GoogleDriveOAuth from connectors.onedrive.oauth import OneDriveOAuth @@ -24,6 +24,13 @@ class AuthService: async def init_oauth(self, connector_type: str, purpose: str, connection_name: str, redirect_uri: str, user_id: str = None) -> dict: """Initialize OAuth flow for authentication or data source connection""" + # Check if we're in no-auth mode + if is_no_auth_mode(): + if purpose == "app_auth": + raise ValueError("OAuth credentials not configured. Please add GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET environment variables to enable authentication.") + else: + raise ValueError("OAuth credentials not configured. Data source connections require OAuth setup.") + # Validate connector_type based on purpose if purpose == "app_auth" and connector_type != "google_drive": raise ValueError("Only Google login supported for app authentication") @@ -253,6 +260,14 @@ class AuthService: async def get_user_info(self, request) -> Optional[dict]: """Get current user information from request""" + # In no-auth mode, return a consistent response + if is_no_auth_mode(): + return { + "authenticated": False, + "user": None, + "no_auth_mode": True + } + user = getattr(request.state, 'user', None) if user: diff --git a/src/services/document_service.py b/src/services/document_service.py index 575201db..6f6b4940 100644 --- a/src/services/document_service.py +++ b/src/services/document_service.py @@ -152,13 +152,18 @@ class DocumentService: "page": chunk["page"], "text": chunk["text"], "chunk_embedding": vect, - "owner": owner_user_id, - "owner_name": owner_name, - "owner_email": owner_email, "file_size": file_size, "connector_type": connector_type, "indexed_time": datetime.datetime.now().isoformat() } + + # Only set owner fields if owner_user_id is provided (for no-auth mode support) + if owner_user_id is not None: + chunk_doc["owner"] = owner_user_id + if owner_name is not None: + chunk_doc["owner_name"] = owner_name + if owner_email is not None: + chunk_doc["owner_email"] = owner_email chunk_id = f"{file_hash}_{i}" try: await opensearch_client.index(index=INDEX_NAME, id=chunk_id, body=chunk_doc) @@ -288,13 +293,18 @@ class DocumentService: "page": chunk["page"], "text": chunk["text"], "chunk_embedding": vect, - "owner": owner_user_id, - "owner_name": owner_name, - "owner_email": owner_email, "file_size": file_size, "connector_type": connector_type, "indexed_time": datetime.datetime.now().isoformat() } + + # Only set owner fields if owner_user_id is provided (for no-auth mode support) + if owner_user_id is not None: + chunk_doc["owner"] = owner_user_id + if owner_name is not None: + chunk_doc["owner_name"] = owner_name + if owner_email is not None: + chunk_doc["owner_email"] = owner_email chunk_id = f"{slim_doc['id']}_{i}" try: await opensearch_client.index(index=INDEX_NAME, id=chunk_id, body=chunk_doc) diff --git a/src/services/search_service.py b/src/services/search_service.py index 1fa70946..85d3e32c 100644 --- a/src/services/search_service.py +++ b/src/services/search_service.py @@ -132,11 +132,13 @@ class SearchService: search_body["min_score"] = score_threshold # Authentication required - DLS will handle document filtering automatically + print(f"[DEBUG] search_service: user_id={user_id}, jwt_token={'None' if jwt_token is None else 'present'}") if not user_id: + print(f"[DEBUG] search_service: user_id is None/empty, returning auth error") return {"results": [], "error": "Authentication required"} - # Get user's OpenSearch client with JWT for OIDC auth - opensearch_client = clients.create_user_opensearch_client(jwt_token) + # Get user's OpenSearch client with JWT for OIDC auth through session manager + opensearch_client = self.session_manager.get_user_opensearch_client(user_id, jwt_token) try: results = await opensearch_client.search(index=INDEX_NAME, body=search_body) @@ -173,7 +175,8 @@ class SearchService: async def search(self, query: str, user_id: str = None, jwt_token: str = None, filters: Dict[str, Any] = None, limit: int = 10, score_threshold: float = 0) -> Dict[str, Any]: """Public search method for API endpoints""" # Set auth context if provided (for direct API calls) - if user_id and jwt_token: + from config.settings import is_no_auth_mode + if user_id and (jwt_token or is_no_auth_mode()): from auth_context import set_auth_context set_auth_context(user_id, jwt_token) diff --git a/src/session_manager.py b/src/session_manager.py index 794067ad..1c3b5835 100644 --- a/src/session_manager.py +++ b/src/session_manager.py @@ -160,9 +160,37 @@ class SessionManager: def get_user_opensearch_client(self, user_id: str, jwt_token: str): """Get or create OpenSearch client for user with their JWT""" + from config.settings import is_no_auth_mode + + print(f"[DEBUG] get_user_opensearch_client: user_id={user_id}, jwt_token={'None' if jwt_token is None else 'present'}, no_auth_mode={is_no_auth_mode()}") + + # In no-auth mode, create anonymous JWT for OpenSearch DLS + if is_no_auth_mode() and jwt_token is None: + if not hasattr(self, '_anonymous_jwt'): + # Create anonymous JWT token for OpenSearch OIDC + print(f"[DEBUG] Creating anonymous JWT...") + self._anonymous_jwt = self._create_anonymous_jwt() + print(f"[DEBUG] Anonymous JWT created: {self._anonymous_jwt[:50]}...") + jwt_token = self._anonymous_jwt + print(f"[DEBUG] Using anonymous JWT for OpenSearch") + # Check if we have a cached client for this user if user_id not in self.user_opensearch_clients: from config.settings import clients self.user_opensearch_clients[user_id] = clients.create_user_opensearch_client(jwt_token) - return self.user_opensearch_clients[user_id] \ No newline at end of file + return self.user_opensearch_clients[user_id] + + def _create_anonymous_jwt(self) -> str: + """Create JWT token for anonymous user in no-auth mode""" + anonymous_user = User( + user_id="anonymous", + email="anonymous@localhost", + name="Anonymous User", + picture=None, + provider="none", + created_at=datetime.now(), + last_login=datetime.now() + ) + + return self.create_jwt_token(anonymous_user) \ No newline at end of file diff --git a/uv.lock b/uv.lock index bac2c8e2..3aee944e 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,8 @@ requires-python = ">=3.13" resolution-markers = [ "sys_platform == 'darwin'", "platform_machine == 'aarch64' and sys_platform == 'linux'", - "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", + "platform_machine == 'x86_64' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", ] [[package]] @@ -372,8 +373,10 @@ dependencies = [ { name = "pydantic" }, { name = "rtree" }, { name = "safetensors", extra = ["torch"] }, - { name = "torch" }, - { name = "torchvision" }, + { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "torchvision", version = "0.22.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torchvision", version = "0.23.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, { name = "tqdm" }, { name = "transformers" }, ] @@ -417,8 +420,10 @@ dependencies = [ { name = "scikit-image" }, { name = "scipy" }, { name = "shapely" }, - { name = "torch" }, - { name = "torchvision" }, + { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "torchvision", version = "0.22.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torchvision", version = "0.23.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/bb/84/4a2cab0e6adde6a85e7ba543862e5fc0250c51f3ac721a078a55cdcff250/easyocr-1.7.2-py3-none-any.whl", hash = "sha256:5be12f9b0e595d443c9c3d10b0542074b50f0ec2d98b141a109cd961fd1c177c", size = 2870178, upload-time = "2024-09-24T11:34:43.554Z" }, @@ -1202,7 +1207,7 @@ name = "nvidia-cudnn-cu12" version = "9.7.1.26" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/25/dc/dc825c4b1c83b538e207e34f48f86063c88deaa35d46c651c7c181364ba2/nvidia_cudnn_cu12-9.7.1.26-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:6d011159a158f3cfc47bf851aea79e31bcff60d530b70ef70474c84cac484d07", size = 726851421, upload-time = "2025-02-06T22:18:29.812Z" }, @@ -1213,7 +1218,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.41" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ac/26/b53c493c38dccb1f1a42e1a21dc12cba2a77fbe36c652f7726d9ec4aba28/nvidia_cufft_cu12-11.3.3.41-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:da650080ab79fcdf7a4b06aa1b460e99860646b176a43f6208099bdc17836b6a", size = 193118795, upload-time = "2025-01-23T17:56:30.536Z" }, @@ -1240,9 +1245,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.2.55" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/08/953675873a136d96bb12f93b49ba045d1107bc94d2551c52b12fa6c7dec3/nvidia_cusolver_cu12-11.7.2.55-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4d1354102f1e922cee9db51920dba9e2559877cf6ff5ad03a00d853adafb191b", size = 260373342, upload-time = "2025-01-23T17:58:56.406Z" }, @@ -1253,7 +1258,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.7.53" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/ab/31e8149c66213b846c082a3b41b1365b831f41191f9f40c6ddbc8a7d550e/nvidia_cusparse_cu12-12.5.7.53-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c1b61eb8c85257ea07e9354606b26397612627fdcd327bfd91ccf6155e7c86d", size = 292064180, upload-time = "2025-01-23T18:00:23.233Z" }, @@ -1386,7 +1391,8 @@ dependencies = [ { name = "pyjwt" }, { name = "python-multipart" }, { name = "starlette" }, - { name = "torch" }, + { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, { name = "uvicorn" }, ] @@ -1407,7 +1413,8 @@ requires-dist = [ { name = "pyjwt", specifier = ">=2.8.0" }, { name = "python-multipart", specifier = ">=0.0.20" }, { name = "starlette", specifier = ">=0.47.1" }, - { name = "torch", specifier = ">=2.7.1", index = "https://download.pytorch.org/whl/cu128" }, + { name = "torch", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'", specifier = ">=2.7.1" }, + { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'", specifier = ">=2.7.1", index = "https://download.pytorch.org/whl/cu128" }, { name = "uvicorn", specifier = ">=0.35.0" }, ] @@ -2094,7 +2101,8 @@ wheels = [ [package.optional-dependencies] torch = [ { name = "numpy" }, - { name = "torch" }, + { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, ] [[package]] @@ -2339,11 +2347,14 @@ wheels = [ name = "torch" version = "2.7.1+cu128" source = { registry = "https://download.pytorch.org/whl/cu128" } +resolution-markers = [ + "platform_machine == 'x86_64' and sys_platform == 'linux'", +] dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, + { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "jinja2", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -2358,38 +2369,85 @@ dependencies = [ { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sympy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ - { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d56d29a6ad7758ba5173cc2b0c51c93e126e2b0a918e874101dc66545283967f" }, { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9560425f9ea1af1791507e8ca70d5b9ecf62fed7ca226a95fcd58d0eb2cca78f" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:500ad5b670483f62d4052e41948a3fb19e8c8de65b99f8d418d879cbb15a82d6" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:f112465fdf42eb1297c6dddda1a8b7f411914428b704e1b8a47870c52e290909" }, { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c355db49c218ada70321d5c5c9bb3077312738b99113c8f3723ef596b554a7b9" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:e27e5f7e74179fb5d814a0412e5026e4b50c9e0081e9050bc4c28c992a276eb1" }, +] + +[[package]] +name = "torch" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform == 'darwin'", + "platform_machine == 'aarch64' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "filelock", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "fsspec", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "jinja2", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "networkx", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "setuptools", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "sympy", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "typing-extensions", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128", size = 101936856, upload-time = "2025-08-06T14:54:01.526Z" }, + { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b", size = 887922844, upload-time = "2025-08-06T14:55:50.78Z" }, + { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16", size = 241330968, upload-time = "2025-08-06T14:54:45.293Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" }, + { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def", size = 102020139, upload-time = "2025-08-06T14:54:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a", size = 887990692, upload-time = "2025-08-06T14:56:18.286Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca", size = 241603453, upload-time = "2025-08-06T14:55:22.945Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" }, ] [[package]] name = "torchvision" version = "0.22.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "platform_machine == 'x86_64' and sys_platform == 'linux'", +] dependencies = [ - { name = "numpy" }, - { name = "pillow" }, - { name = "torch" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/30/fecdd09fb973e963da68207fe9f3d03ec6f39a935516dc2a98397bf495c6/torchvision-0.22.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c3ae3319624c43cc8127020f46c14aa878406781f0899bb6283ae474afeafbf", size = 1947818, upload-time = "2025-06-04T17:42:51.954Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/b45f6cd92fa0acfac5e31b8e9258232f25bcdb0709a604e8b8a39d76e411/torchvision-0.22.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:4a614a6a408d2ed74208d0ea6c28a2fbb68290e9a7df206c5fef3f0b6865d307", size = 2471597, upload-time = "2025-06-04T17:42:48.838Z" }, { url = "https://files.pythonhosted.org/packages/8d/b0/3cffd6a285b5ffee3fe4a31caff49e350c98c5963854474d1c4f7a51dea5/torchvision-0.22.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7ee682be589bb1a002b7704f06b8ec0b89e4b9068f48e79307d2c6e937a9fdf4", size = 7485894, upload-time = "2025-06-04T17:43:01.371Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1d/0ede596fedc2080d18108149921278b59f220fbb398f29619495337b0f86/torchvision-0.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:2566cafcfa47ecfdbeed04bab8cef1307c8d4ef75046f7624b9e55f384880dfe", size = 1708020, upload-time = "2025-06-04T17:43:06.085Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ca/e9a06bd61ee8e04fb4962a3fb524fe6ee4051662db07840b702a9f339b24/torchvision-0.22.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:043d9e35ed69c2e586aff6eb9e2887382e7863707115668ac9d140da58f42cba", size = 2137623, upload-time = "2025-06-04T17:43:05.028Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c8/2ebe90f18e7ffa2120f5c3eab62aa86923185f78d2d051a455ea91461608/torchvision-0.22.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:27142bcc8a984227a6dcf560985e83f52b82a7d3f5fe9051af586a2ccc46ef26", size = 2476561, upload-time = "2025-06-04T17:42:59.691Z" }, { url = "https://files.pythonhosted.org/packages/94/8b/04c6b15f8c29b39f0679589753091cec8b192ab296d4fdaf9055544c4ec9/torchvision-0.22.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ef46e065502f7300ad6abc98554131c35dc4c837b978d91306658f1a65c00baa", size = 7658543, upload-time = "2025-06-04T17:42:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c0/131628e6d42682b0502c63fd7f647b8b5ca4bd94088f6c85ca7225db8ac4/torchvision-0.22.1-cp313-cp313t-win_amd64.whl", hash = "sha256:7414eeacfb941fa21acddcd725f1617da5630ec822e498660a4b864d7d998075", size = 1629892, upload-time = "2025-06-04T17:42:57.156Z" }, +] + +[[package]] +name = "torchvision" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform == 'darwin'", + "platform_machine == 'aarch64' and sys_platform == 'linux'", + "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "pillow", marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/37/45a5b9407a7900f71d61b2b2f62db4b7c632debca397f205fdcacb502780/torchvision-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1c37e325e09a184b730c3ef51424f383ec5745378dc0eca244520aca29722600", size = 1856886, upload-time = "2025-08-06T14:58:05.491Z" }, + { url = "https://files.pythonhosted.org/packages/ac/da/a06c60fc84fc849377cf035d3b3e9a1c896d52dbad493b963c0f1cdd74d0/torchvision-0.23.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2f7fd6c15f3697e80627b77934f77705f3bc0e98278b989b2655de01f6903e1d", size = 2353112, upload-time = "2025-08-06T14:58:26.265Z" }, + { url = "https://files.pythonhosted.org/packages/a0/27/5ce65ba5c9d3b7d2ccdd79892ab86a2f87ac2ca6638f04bb0280321f1a9c/torchvision-0.23.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a76fafe113b2977be3a21bf78f115438c1f88631d7a87203acb3dd6ae55889e6", size = 8627658, upload-time = "2025-08-06T14:58:15.999Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e4/028a27b60aa578a2fa99d9d7334ff1871bb17008693ea055a2fdee96da0d/torchvision-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:07d069cb29691ff566e3b7f11f20d91044f079e1dbdc9d72e0655899a9b06938", size = 1600749, upload-time = "2025-08-06T14:58:10.719Z" }, + { url = "https://files.pythonhosted.org/packages/05/35/72f91ad9ac7c19a849dedf083d347dc1123f0adeb401f53974f84f1d04c8/torchvision-0.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2df618e1143805a7673aaf82cb5720dd9112d4e771983156aaf2ffff692eebf9", size = 2047192, upload-time = "2025-08-06T14:58:11.813Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9d/406cea60a9eb9882145bcd62a184ee61e823e8e1d550cdc3c3ea866a9445/torchvision-0.23.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a3299d2b1d5a7aed2d3b6ffb69c672ca8830671967eb1cee1497bacd82fe47b", size = 2359295, upload-time = "2025-08-06T14:58:17.469Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f4/34662f71a70fa1e59de99772142f22257ca750de05ccb400b8d2e3809c1d/torchvision-0.23.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:76bc4c0b63d5114aa81281390f8472a12a6a35ce9906e67ea6044e5af4cab60c", size = 8800474, upload-time = "2025-08-06T14:58:22.53Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f5/b5a2d841a8d228b5dbda6d524704408e19e7ca6b7bb0f24490e081da1fa1/torchvision-0.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b9e2dabf0da9c8aa9ea241afb63a8f3e98489e706b22ac3f30416a1be377153b", size = 1527667, upload-time = "2025-08-06T14:58:14.446Z" }, ] [[package]] @@ -2430,7 +2488,7 @@ name = "triton" version = "3.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "setuptools", marker = "sys_platform != 'darwin'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/74/1f/dfb531f90a2d367d914adfee771babbd3f1a5b26c3f5fbc458dee21daa78/triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240", size = 155673035, upload-time = "2025-05-29T23:40:02.468Z" },