mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-30 00:22:10 -04:00
feat: add ChatGPT Subscription provider (#2876)
* feat: Add ChatGPT Subscription support and related features - Introduced a new provider option for ChatGPT Subscription in the endpoint selection UI. - Implemented OAuth flow for ChatGPT Subscription sign-in, including polling for authorization status. - Updated admin interface to handle ChatGPT Subscription, including disabling API key input and providing user guidance. - Enhanced cost tracking logic to differentiate between subscription and non-subscription endpoints. - Added new slash commands for managing skills, including listing, searching, and invoking skills. - Implemented caching for skill catalog to optimize performance. - Updated tests to cover new ChatGPT Subscription functionality and ensure proper endpoint probing. - Refactored existing code to accommodate new features and improve maintainability. * refactor: share provider device-flow setup - reuse one device-flow backend for Copilot and ChatGPT Subscription - add one frontend device-flow helper for Settings and /setup - put GitHub Copilot back into Add Models, now as a dropdown option - make provider selection just select; clicking Add starts sign-in - stop ChatGPT Subscription setup from opening auth tabs automatically - make /setup copilot and /setup chatgpt-subscription work from chat - show ChatGPT Subscription in the /setup suggestions - show the real error message when setup fails - add focused tests for the shared flow and setup UI * feat(chatgpt-subscription): harden credential lifecycle and streamline auth UX Backend: - Resolve runtime bearer for provider-auth endpoints at probe time via a shared _resolve_probe_key() that delegates to resolve_endpoint_runtime, applied across all probe/refresh call sites. - Skip live completion probes and health pings for discovery-only providers (centralized behind _is_discovery_only_provider) — the Codex/Responses API has no such endpoints, so status is derived from cached models. - Never persist the short lived ChatGPT bearer to the plaintext sessions table; proactively clear any stale bearer left by an earlier code path. - Revoke orphaned ProviderAuthSession credentials when the last endpoint backing them is deleted (_delete_orphaned_provider_auth), surfaced via cleared_provider_auth in the delete response. Frontend (admin.js): - Auto-start the device-auth flow on provider selection so the authorization panel (code + Authorize) shows immediately instead of behind a "Sign in" click. - Remove the redundant top button for device auth providers, move retry into the panel via an inline "Try again". - Drop the self-evident hint text and add an execCommand clipboard fallback so Copy works in non-secure (HTTP/LAN) contexts. * fix: harden chatgpt subscription provider * chore: remove PR media from branch * Fix chatgpt subscription recovery and token handling --------- Co-authored-by: 5p00kyy <admin@5p00ky.dev>
This commit is contained in:
+22
-17
@@ -92,18 +92,13 @@ def _reject_compact_during_active_run(session_id: str) -> None:
|
||||
|
||||
|
||||
def _verify_session_owner(request: Request, session_id: str, session_manager=None):
|
||||
"""Verify the current user owns the session. Raises 404 if not.
|
||||
"""Verify the current user owns the session, honoring single-user modes.
|
||||
|
||||
Ownership is checked against the DB row when one exists (unchanged). If
|
||||
there is no DB row but the caller owns an in-memory "ghost" session — one
|
||||
that lives only in ``session_manager`` because it was never persisted, or
|
||||
its DB row was removed out-of-band — fall back to the in-memory owner so the
|
||||
user can still manage and delete it. Without this fallback such sessions are
|
||||
listed by ``/api/sessions`` (they come from the in-memory manager) yet every
|
||||
per-session operation 404s, making them impossible to delete (issue #1044).
|
||||
|
||||
``session_manager`` is optional and defaults to ``None`` so existing callers
|
||||
that only care about persisted sessions keep their exact prior behavior.
|
||||
Authenticated requests must match the stored DB or in-memory owner. When
|
||||
auth is disabled and no user is present, treat the app as single-user mode:
|
||||
verify that the session exists, but do not compare its stored owner. This
|
||||
keeps QA/dev instances with AUTH_ENABLED=false from rejecting owner-stamped
|
||||
rows created while auth was previously enabled.
|
||||
"""
|
||||
user = effective_user(request)
|
||||
if not user and not _auth_disabled():
|
||||
@@ -114,13 +109,13 @@ def _verify_session_owner(request: Request, session_id: str, session_manager=Non
|
||||
finally:
|
||||
db.close()
|
||||
if row is not None:
|
||||
if row.owner != user:
|
||||
if user and row.owner != user:
|
||||
raise HTTPException(404, f"Session {session_id} not found")
|
||||
return
|
||||
# No DB row — allow the caller to act on an in-memory ghost they own.
|
||||
if session_manager is not None:
|
||||
ghost = getattr(session_manager, "sessions", {}).get(session_id)
|
||||
if ghost is not None and getattr(ghost, "owner", None) == user:
|
||||
if ghost is not None and (not user or getattr(ghost, "owner", None) == user):
|
||||
return
|
||||
raise HTTPException(404, f"Session {session_id} not found")
|
||||
|
||||
@@ -372,8 +367,13 @@ def setup_session_routes(session_manager: SessionManager, config: dict, webhook_
|
||||
pass
|
||||
elif not model_to_use:
|
||||
from src.llm_core import list_model_ids
|
||||
ids = list_model_ids(endpoint_url, timeout=REQUEST_TIMEOUT,
|
||||
headers=validation_headers)
|
||||
ids = list_model_ids(
|
||||
endpoint_url,
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
headers=validation_headers,
|
||||
owner=user,
|
||||
endpoint_id=endpoint_id.strip() if endpoint_id else None,
|
||||
)
|
||||
if not ids:
|
||||
raise HTTPException(400, "Cannot reach /v1/models")
|
||||
# Default to the first CHAT model — endpoints often list embedding/
|
||||
@@ -387,8 +387,13 @@ def setup_session_routes(session_manager: SessionManager, config: dict, webhook_
|
||||
from src.llm_core import list_model_ids
|
||||
import os as _os
|
||||
req_base = _os.path.basename(model_to_use.rstrip("/"))
|
||||
avail = list_model_ids(endpoint_url, timeout=REQUEST_TIMEOUT,
|
||||
headers=validation_headers)
|
||||
avail = list_model_ids(
|
||||
endpoint_url,
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
headers=validation_headers,
|
||||
owner=user,
|
||||
endpoint_id=endpoint_id.strip() if endpoint_id else None,
|
||||
)
|
||||
if not avail:
|
||||
raise HTTPException(400, "Cannot reach /v1/models")
|
||||
if model_to_use not in avail:
|
||||
|
||||
Reference in New Issue
Block a user