merge: resolve conflicts with main (URL update to hermes-agent.nousresearch.com)
This commit is contained in:
commit
e976879cf2
29 changed files with 3110 additions and 115 deletions
43
run_agent.py
43
run_agent.py
|
|
@ -454,7 +454,7 @@ class AIAgent:
|
|||
effective_base = base_url
|
||||
if "openrouter" in effective_base.lower():
|
||||
client_kwargs["default_headers"] = {
|
||||
"HTTP-Referer": "https://github.com/NousResearch/hermes-agent",
|
||||
"HTTP-Referer": "https://hermes-agent.nousresearch.com",
|
||||
"X-OpenRouter-Title": "Hermes Agent",
|
||||
"X-OpenRouter-Categories": "productivity,cli-agent",
|
||||
}
|
||||
|
|
@ -481,7 +481,7 @@ class AIAgent:
|
|||
"api_key": os.getenv("OPENROUTER_API_KEY", ""),
|
||||
"base_url": OPENROUTER_BASE_URL,
|
||||
"default_headers": {
|
||||
"HTTP-Referer": "https://github.com/NousResearch/hermes-agent",
|
||||
"HTTP-Referer": "https://hermes-agent.nousresearch.com",
|
||||
"X-OpenRouter-Title": "Hermes Agent",
|
||||
"X-OpenRouter-Categories": "productivity,cli-agent",
|
||||
},
|
||||
|
|
@ -687,7 +687,7 @@ class AIAgent:
|
|||
# Initialize context compressor for automatic context management
|
||||
# Compresses conversation when approaching model's context limit
|
||||
# Configuration via config.yaml (compression section) or environment variables
|
||||
compression_threshold = float(os.getenv("CONTEXT_COMPRESSION_THRESHOLD", "0.85"))
|
||||
compression_threshold = float(os.getenv("CONTEXT_COMPRESSION_THRESHOLD", "0.50"))
|
||||
compression_enabled = os.getenv("CONTEXT_COMPRESSION_ENABLED", "true").lower() in ("true", "1", "yes")
|
||||
compression_summary_model = os.getenv("CONTEXT_COMPRESSION_MODEL") or None
|
||||
|
||||
|
|
@ -2585,6 +2585,31 @@ class AIAgent:
|
|||
|
||||
return msg
|
||||
|
||||
@staticmethod
|
||||
def _sanitize_tool_calls_for_strict_api(api_msg: dict) -> dict:
|
||||
"""Strip Codex Responses API fields from tool_calls for strict providers.
|
||||
|
||||
Providers like Mistral strictly validate the Chat Completions schema
|
||||
and reject unknown fields (call_id, response_item_id) with 422.
|
||||
These fields are preserved in the internal message history — this
|
||||
method only modifies the outgoing API copy.
|
||||
|
||||
Creates new tool_call dicts rather than mutating in-place, so the
|
||||
original messages list retains call_id/response_item_id for Codex
|
||||
Responses API compatibility (e.g. if the session falls back to a
|
||||
Codex provider later).
|
||||
"""
|
||||
tool_calls = api_msg.get("tool_calls")
|
||||
if not isinstance(tool_calls, list):
|
||||
return api_msg
|
||||
_STRIP_KEYS = {"call_id", "response_item_id"}
|
||||
api_msg["tool_calls"] = [
|
||||
{k: v for k, v in tc.items() if k not in _STRIP_KEYS}
|
||||
if isinstance(tc, dict) else tc
|
||||
for tc in tool_calls
|
||||
]
|
||||
return api_msg
|
||||
|
||||
def flush_memories(self, messages: list = None, min_turns: int = None):
|
||||
"""Give the model one turn to persist memories before context is lost.
|
||||
|
||||
|
|
@ -2622,6 +2647,7 @@ class AIAgent:
|
|||
|
||||
try:
|
||||
# Build API messages for the flush call
|
||||
_is_strict_api = "api.mistral.ai" in self.base_url.lower()
|
||||
api_messages = []
|
||||
for msg in messages:
|
||||
api_msg = msg.copy()
|
||||
|
|
@ -2632,6 +2658,8 @@ class AIAgent:
|
|||
api_msg.pop("reasoning", None)
|
||||
api_msg.pop("finish_reason", None)
|
||||
api_msg.pop("_flush_sentinel", None)
|
||||
if _is_strict_api:
|
||||
self._sanitize_tool_calls_for_strict_api(api_msg)
|
||||
api_messages.append(api_msg)
|
||||
|
||||
if self._cached_system_prompt:
|
||||
|
|
@ -3111,11 +3139,14 @@ class AIAgent:
|
|||
try:
|
||||
# Build API messages, stripping internal-only fields
|
||||
# (finish_reason, reasoning) that strict APIs like Mistral reject with 422
|
||||
_is_strict_api = "api.mistral.ai" in self.base_url.lower()
|
||||
api_messages = []
|
||||
for msg in messages:
|
||||
api_msg = msg.copy()
|
||||
for internal_field in ("reasoning", "finish_reason"):
|
||||
api_msg.pop(internal_field, None)
|
||||
if _is_strict_api:
|
||||
self._sanitize_tool_calls_for_strict_api(api_msg)
|
||||
api_messages.append(api_msg)
|
||||
|
||||
effective_system = self._cached_system_prompt or ""
|
||||
|
|
@ -3509,6 +3540,12 @@ class AIAgent:
|
|||
# Remove finish_reason - not accepted by strict APIs (e.g. Mistral)
|
||||
if "finish_reason" in api_msg:
|
||||
api_msg.pop("finish_reason")
|
||||
# Strip Codex Responses API fields (call_id, response_item_id) for
|
||||
# strict providers like Mistral that reject unknown fields with 422.
|
||||
# Uses new dicts so the internal messages list retains the fields
|
||||
# for Codex Responses compatibility.
|
||||
if "api.mistral.ai" in self.base_url.lower():
|
||||
self._sanitize_tool_calls_for_strict_api(api_msg)
|
||||
# Keep 'reasoning_details' - OpenRouter uses this for multi-turn reasoning context
|
||||
# The signature field helps maintain reasoning continuity
|
||||
api_messages.append(api_msg)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue