Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions hindsight-api-slim/hindsight_api/engine/embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,6 @@ def encode(self, texts: list[str]) -> list[list[float]]:
"""
from openai import AuthenticationError

from .providers.codex_auth import CodexRefreshExpiredError

# Proactive refresh — cheap when fresh (JWT exp decode + compare).
self._auth_manager.ensure_fresh_token()
if self._auth_manager.access_token != self.api_key:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,15 @@ def from_file(cls, auth_file: Path | None = None) -> "CodexAuthManager":
# Token state helpers
# ------------------------------------------------------------------

def load_refresh_token_from_file(self) -> str | None:
"""Re-read ``tokens.refresh_token`` from ``_auth_file``.
@staticmethod
def load_refresh_token_from_file(auth_file: Path) -> str | None:
"""Read ``tokens.refresh_token`` from ``auth_file``.

Returns ``None`` when the file is unreadable or omits the field.
Does not raise — the provider degrades to one-shot mode.
"""
try:
with open(self._auth_file) as f:
with open(auth_file) as f:
data = json.load(f)
except (OSError, json.JSONDecodeError) as e:
logger.warning(
Expand Down
25 changes: 8 additions & 17 deletions hindsight-api-slim/hindsight_api/engine/providers/codex_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,26 +190,17 @@ def _load_codex_auth(self) -> tuple[str, str]:
return access_token, account_id

def _load_codex_refresh_token(self) -> str | None:
"""Delegate to ``_auth_manager.load_refresh_token_from_file()``.
"""Read ``tokens.refresh_token`` from the configured auth file.

Kept as an instance method so existing tests that patch
``CodexLLM._load_codex_refresh_token`` continue to work.
``CodexLLM._load_codex_refresh_token`` continue to work. Works both
pre- and post-``__init__`` because it does not depend on
``_auth_manager`` being constructed yet.
"""
# During __init__ the manager doesn't exist yet; fall back to reading
# from the default auth file path directly.
if not hasattr(self, "_auth_manager"):
auth_file = Path.home() / ".codex" / "auth.json"
try:
with open(auth_file) as f:
data = json.load(f)
except (OSError, json.JSONDecodeError) as e:
logger.warning(
f"Codex auth file unreadable when loading refresh_token: {type(e).__name__}. "
"Token refresh will not be available; the access_token in memory will be used until it expires."
)
return None
return data.get("tokens", {}).get("refresh_token")
return self._auth_manager.load_refresh_token_from_file()
auth_file = (
self._auth_manager._auth_file if hasattr(self, "_auth_manager") else Path.home() / ".codex" / "auth.json"
)
return CodexAuthManager.load_refresh_token_from_file(auth_file)

@staticmethod
def _decode_jwt_exp_unixtime(token: str) -> int | None:
Expand Down
12 changes: 4 additions & 8 deletions hindsight-api-slim/tests/test_codex_oauth_refresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,17 +554,14 @@ def _make_codex_auth_file(tmp_path: Path, access_token: str, refresh_token: str
def test_codex_oauth_embeddings_picks_up_refreshed_token_on_encode(tmp_path: Path, monkeypatch):
"""encode() calls ensure_fresh_token() and updates api_key when the token rotated."""
from hindsight_api.engine.embeddings import CodexOAuthEmbeddings
from hindsight_api.engine.providers.codex_auth import CodexAuthManager

expired = _make_jwt(int(time.time()) - 60)
new_access = _make_jwt(int(time.time()) + 3600)

auth_file = _make_codex_auth_file(tmp_path, expired, refresh_token="rt-embed")
monkeypatch.setenv("HOME", str(tmp_path))
_make_codex_auth_file(tmp_path, expired, refresh_token="rt-embed")
monkeypatch.setattr(Path, "home", lambda: tmp_path)

emb = CodexOAuthEmbeddings(model="text-embedding-3-small", batch_size=10)
# Manually point to our tmp auth file after construction.
emb._auth_manager._auth_file = auth_file

refresh_resp = _refresh_response(200, {"access_token": new_access, "refresh_token": "rt-new"})

Expand Down Expand Up @@ -592,11 +589,10 @@ def test_codex_oauth_embeddings_reactive_refresh_on_401(tmp_path: Path, monkeypa
fresh = _make_jwt(int(time.time()) + 3600)
new_access = _make_jwt(int(time.time()) + 7200)

auth_file = _make_codex_auth_file(tmp_path, fresh, refresh_token="rt-embed")
monkeypatch.setenv("HOME", str(tmp_path))
_make_codex_auth_file(tmp_path, fresh, refresh_token="rt-embed")
monkeypatch.setattr(Path, "home", lambda: tmp_path)

emb = CodexOAuthEmbeddings(model="text-embedding-3-small", batch_size=10)
emb._auth_manager._auth_file = auth_file
emb._dimension = 1536

refresh_resp = _refresh_response(200, {"access_token": new_access, "refresh_token": "rt-rotated"})
Expand Down
Loading