Merge origin/main into codex/align-codex-provider-conventions-mainrepo

This commit is contained in:
George Pickett 2026-02-25 19:28:44 -08:00
commit e71d9a89d2
5 changed files with 40 additions and 1 deletions

View file

@ -35,6 +35,14 @@ _OR_HEADERS = {
"X-OpenRouter-Categories": "cli-agent", "X-OpenRouter-Categories": "cli-agent",
} }
# Nous Portal extra_body for product attribution.
# Callers should pass this as extra_body in chat.completions.create()
# when the auxiliary client is backed by Nous Portal.
NOUS_EXTRA_BODY = {"tags": ["product=hermes-agent"]}
# Set at resolve time — True if the auxiliary client points to Nous Portal
auxiliary_is_nous: bool = False
# Default auxiliary models per provider # Default auxiliary models per provider
_OPENROUTER_MODEL = "google/gemini-3-flash-preview" _OPENROUTER_MODEL = "google/gemini-3-flash-preview"
_NOUS_MODEL = "gemini-3-flash" _NOUS_MODEL = "gemini-3-flash"
@ -91,6 +99,8 @@ def get_text_auxiliary_client() -> Tuple[Optional[OpenAI], Optional[str]]:
# 2. Nous Portal # 2. Nous Portal
nous = _read_nous_auth() nous = _read_nous_auth()
if nous: if nous:
global auxiliary_is_nous
auxiliary_is_nous = True
logger.debug("Auxiliary text client: Nous Portal") logger.debug("Auxiliary text client: Nous Portal")
return ( return (
OpenAI(api_key=_nous_api_key(nous), base_url=_nous_base_url()), OpenAI(api_key=_nous_api_key(nous), base_url=_nous_base_url()),
@ -135,3 +145,12 @@ def get_vision_auxiliary_client() -> Tuple[Optional[OpenAI], Optional[str]]:
# 3. Nothing suitable # 3. Nothing suitable
logger.debug("Auxiliary vision client: none available") logger.debug("Auxiliary vision client: none available")
return None, None return None, None
def get_auxiliary_extra_body() -> dict:
"""Return extra_body kwargs for auxiliary API calls.
Includes Nous Portal product tags when the auxiliary client is backed
by Nous Portal. Returns empty dict otherwise.
"""
return dict(NOUS_EXTRA_BODY) if auxiliary_is_nous else {}

View file

@ -25,7 +25,15 @@ def get_async_client() -> AsyncOpenAI:
api_key = os.getenv("OPENROUTER_API_KEY") api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key: if not api_key:
raise ValueError("OPENROUTER_API_KEY environment variable not set") raise ValueError("OPENROUTER_API_KEY environment variable not set")
_client = AsyncOpenAI(api_key=api_key, base_url=OPENROUTER_BASE_URL) _client = AsyncOpenAI(
api_key=api_key,
base_url=OPENROUTER_BASE_URL,
default_headers={
"HTTP-Referer": "https://github.com/NousResearch/hermes-agent",
"X-OpenRouter-Title": "Hermes Agent",
"X-OpenRouter-Categories": "cli-agent",
},
)
return _client return _client

View file

@ -170,12 +170,15 @@ async def _summarize_session(
max_retries = 3 max_retries = 3
for attempt in range(max_retries): for attempt in range(max_retries):
try: try:
from agent.auxiliary_client import get_auxiliary_extra_body
_extra = get_auxiliary_extra_body()
response = await _async_aux_client.chat.completions.create( response = await _async_aux_client.chat.completions.create(
model=_SUMMARIZER_MODEL, model=_SUMMARIZER_MODEL,
messages=[ messages=[
{"role": "system", "content": system_prompt}, {"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}, {"role": "user", "content": user_prompt},
], ],
**({} if not _extra else {"extra_body": _extra}),
temperature=0.1, temperature=0.1,
max_tokens=MAX_SUMMARY_TOKENS, max_tokens=MAX_SUMMARY_TOKENS,
) )

View file

@ -314,11 +314,14 @@ async def vision_analyze_tool(
logger.info("Processing image with %s...", model) logger.info("Processing image with %s...", model)
# Call the vision API # Call the vision API
from agent.auxiliary_client import get_auxiliary_extra_body
_extra = get_auxiliary_extra_body()
response = await _aux_async_client.chat.completions.create( response = await _aux_async_client.chat.completions.create(
model=model, model=model,
messages=messages, messages=messages,
temperature=0.1, temperature=0.1,
max_tokens=2000, max_tokens=2000,
**({} if not _extra else {"extra_body": _extra}),
) )
# Extract the analysis # Extract the analysis

View file

@ -242,6 +242,8 @@ Create a markdown summary that captures all key information in a well-organized,
if _aux_async_client is None: if _aux_async_client is None:
logger.warning("No auxiliary model available for web content processing") logger.warning("No auxiliary model available for web content processing")
return None return None
from agent.auxiliary_client import get_auxiliary_extra_body
_extra = get_auxiliary_extra_body()
response = await _aux_async_client.chat.completions.create( response = await _aux_async_client.chat.completions.create(
model=model, model=model,
messages=[ messages=[
@ -250,6 +252,7 @@ Create a markdown summary that captures all key information in a well-organized,
], ],
temperature=0.1, temperature=0.1,
max_tokens=max_tokens, max_tokens=max_tokens,
**({} if not _extra else {"extra_body": _extra}),
) )
return response.choices[0].message.content.strip() return response.choices[0].message.content.strip()
except Exception as api_error: except Exception as api_error:
@ -362,6 +365,8 @@ Create a single, unified markdown summary."""
fallback = fallback[:max_output_size] + "\n\n[... truncated ...]" fallback = fallback[:max_output_size] + "\n\n[... truncated ...]"
return fallback return fallback
from agent.auxiliary_client import get_auxiliary_extra_body
_extra = get_auxiliary_extra_body()
response = await _aux_async_client.chat.completions.create( response = await _aux_async_client.chat.completions.create(
model=model, model=model,
messages=[ messages=[
@ -370,6 +375,7 @@ Create a single, unified markdown summary."""
], ],
temperature=0.1, temperature=0.1,
max_tokens=4000, max_tokens=4000,
**({} if not _extra else {"extra_body": _extra}),
) )
final_summary = response.choices[0].message.content.strip() final_summary = response.choices[0].message.content.strip()