@@ -226,7 +226,7 @@ def on_session_end(self, messages: List[Dict[str, Any]]) -> None:
226226
227227 def _save_session_async (self , messages : List [Dict [str , Any ]]) -> None :
228228 try :
229- import anthropic , re
229+ import re , requests
230230
231231 turns = []
232232 for m in messages :
@@ -243,7 +243,8 @@ def _save_session_async(self, messages: List[Dict[str, Any]]) -> None:
243243 return
244244
245245 conversation = "\n \n " .join (turns [- 20 :])
246- client = anthropic .Anthropic (api_key = api_key )
246+ provider = self ._cfg .get ("llm_provider" , "openai" )
247+ model = self ._cfg .get ("llm_model" , "gpt-5.4-mini" )
247248 prompt = (
248249 "Summarize this conversation in 2 sentences max. "
249250 "Then list any unresolved threads or follow-up items as a bullet list (max 5). "
@@ -253,12 +254,34 @@ def _save_session_async(self, messages: List[Dict[str, Any]]) -> None:
253254 f"CONVERSATION:\n { conversation } "
254255 )
255256
256- msg = client .messages .create (
257- model = self ._cfg .get ("llm_model" , "claude-haiku-4-5" ),
258- max_tokens = 500 ,
259- messages = [{"role" : "user" , "content" : prompt }],
260- )
261- raw = msg .content [0 ].text .strip ()
257+ if provider == "anthropic" :
258+ import anthropic
259+ client = anthropic .Anthropic (api_key = api_key )
260+ msg = client .messages .create (
261+ model = model ,
262+ max_tokens = 500 ,
263+ messages = [{"role" : "user" , "content" : prompt }],
264+ )
265+ raw = msg .content [0 ].text .strip ()
266+ else :
267+ # OpenAI-compatible (uses whatever base_url the Hermes provider exposes)
268+ base_url = getattr (self , "_api_base_url" , None ) or "https://api.openai.com/v1"
269+ resp = requests .post (
270+ f"{ base_url .rstrip ('/' )} /chat/completions" ,
271+ headers = {
272+ "Authorization" : f"Bearer { api_key } " ,
273+ "Content-Type" : "application/json" ,
274+ },
275+ json = {
276+ "model" : model ,
277+ "max_tokens" : 500 ,
278+ "messages" : [{"role" : "user" , "content" : prompt }],
279+ },
280+ timeout = 30 ,
281+ )
282+ resp .raise_for_status ()
283+ raw = resp .json ()["choices" ][0 ]["message" ]["content" ].strip ()
284+
262285 raw = re .sub (r"^```(?:json)?\s*" , "" , raw )
263286 raw = re .sub (r"\s*```$" , "" , raw )
264287 data = json .loads (raw )
@@ -303,9 +326,75 @@ def _regenerate_briefing(self) -> None:
303326 logger .debug ("Engram: briefing regen failed: %s" , e )
304327
305328 def _get_api_key (self ) -> Optional [str ]:
329+ """Resolve an API key and base URL for session summarization.
330+
331+ Priority:
332+ 1. ANTHROPIC_API_KEY env var (for Anthropic provider)
333+ 2. Hermes credential pool (reads active provider from config.yaml)
334+ 3. Codex OAuth access token from ~/.codex/auth.json
335+ 4. .env file fallback
336+
337+ Sets self._api_base_url for the resolved provider.
338+ """
339+ provider = self ._cfg .get ("llm_provider" , "hermes" )
340+ self ._api_base_url = None
341+
342+ # Read active provider from Hermes config
343+ hermes_cfg_path = Path ("~/.hermes/config.yaml" ).expanduser ()
344+ hermes_provider = None
345+ hermes_base_url = None
346+ if hermes_cfg_path .exists ():
347+ try :
348+ import yaml
349+ cfg = yaml .safe_load (hermes_cfg_path .read_text ()) or {}
350+ hermes_provider = cfg .get ("model" , {}).get ("provider" , "" )
351+ hermes_base_url = cfg .get ("model" , {}).get ("base_url" , "" )
352+ except Exception :
353+ pass
354+
355+ if provider != "anthropic" :
356+ # Try Hermes credential pool — match active provider first
357+ auth_path = Path ("~/.hermes/auth.json" ).expanduser ()
358+ if auth_path .exists ():
359+ try :
360+ data = json .loads (auth_path .read_text ())
361+ pool = data .get ("credential_pool" , {})
362+ # Try the active Hermes provider first
363+ if hermes_provider and hermes_provider in pool :
364+ for creds in pool [hermes_provider ]:
365+ token = creds .get ("access_token" , "" )
366+ if token and len (token ) > 10 :
367+ self ._api_base_url = creds .get ("base_url" ) or hermes_base_url
368+ return token
369+ # Fallback: scan all providers
370+ for prov_name , creds_list in pool .items ():
371+ if prov_name == "anthropic" :
372+ continue
373+ for creds in creds_list :
374+ token = creds .get ("access_token" , "" )
375+ if token and len (token ) > 10 :
376+ self ._api_base_url = creds .get ("base_url" ) or hermes_base_url
377+ return token
378+ except Exception :
379+ pass
380+
381+ # Codex OAuth token
382+ codex_path = Path ("~/.codex/auth.json" ).expanduser ()
383+ if codex_path .exists ():
384+ try :
385+ data = json .loads (codex_path .read_text ())
386+ token = data .get ("accessToken" , "" )
387+ if token and len (token ) > 10 :
388+ return token
389+ except Exception :
390+ pass
391+
392+ # Anthropic env var
306393 key = os .environ .get ("ANTHROPIC_API_KEY" , "" )
307394 if key and key != "***" :
308395 return key
396+
397+ # .env file
309398 env_path = Path ("~/.hermes/.env" ).expanduser ()
310399 if env_path .exists ():
311400 for line in env_path .read_text ().splitlines ():
0 commit comments