Skip to content
Closed
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
15 changes: 15 additions & 0 deletions agent/chat_completion_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,21 @@ def handle_max_iterations(agent, messages: list, api_call_count: int) -> str:
# turns so Anthropic-family providers don't 400 the summary call.
api_messages = agent._drop_thinking_only_and_merge_users(api_messages)

# The max-iterations summary path calls chat.completions.create()
# directly instead of going through _build_api_kwargs(), so apply the
# same ProviderProfile message hook here. This keeps provider-gated
# outbound prompt fixes (for example Z.AI's prompt-sensitive 1305
# mitigation) from being bypassed during summary generation.
if agent.api_mode == "chat_completions":
try:
from providers import get_provider_profile

profile = get_provider_profile(agent.provider)
if profile:
api_messages = profile.prepare_messages(api_messages)
except Exception as exc:
logger.debug("Summary provider message preparation skipped: %s", exc)

summary_extra_body = {}
try:
from agent.auxiliary_client import _fixed_temperature_for_model, OMIT_TEMPERATURE as _OMIT_TEMP
Expand Down
70 changes: 69 additions & 1 deletion plugins/model-providers/zai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,77 @@
"""ZAI / GLM provider profile."""

from typing import Any

from providers import register_provider
from providers.base import ProviderProfile

zai = ProviderProfile(

_BLOCKED_SYSTEM_PROMPT_PHRASE = "Hermes Agent"
_SAFE_SYSTEM_PROMPT_PHRASE = "Hermes framework"


def _sanitize_system_prompt_content(content: Any) -> tuple[Any, bool]:
if isinstance(content, str):
if _BLOCKED_SYSTEM_PROMPT_PHRASE not in content:
return content, False
return (
content.replace(_BLOCKED_SYSTEM_PROMPT_PHRASE, _SAFE_SYSTEM_PROMPT_PHRASE),
True,
)

if not isinstance(content, list):
return content, False

changed = False
sanitized_parts = []
for part in content:
if (
isinstance(part, dict)
and isinstance(part.get("text"), str)
and _BLOCKED_SYSTEM_PROMPT_PHRASE in part["text"]
):
sanitized_parts.append(
{
**part,
"text": part["text"].replace(
_BLOCKED_SYSTEM_PROMPT_PHRASE,
_SAFE_SYSTEM_PROMPT_PHRASE,
),
}
)
changed = True
else:
sanitized_parts.append(part)
return (sanitized_parts, True) if changed else (content, False)


class ZaiProviderProfile(ProviderProfile):
def prepare_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Avoid Z.AI Coding Plan's prompt-sensitive 1305 false overload.

Z.AI GLM Coding Plan rejects requests whose effective system prompt
contains the exact phrase "Hermes Agent" with HTTP 429 / code 1305.
Rewrite only outbound system/developer prompt copies so cached Hermes
prompts, history, and user text remain unchanged.
"""
sanitized_messages = None
for index, message in enumerate(messages):
if (
not isinstance(message, dict)
or message.get("role") not in {"system", "developer"}
):
continue
content, changed = _sanitize_system_prompt_content(message.get("content"))
if not changed:
continue
if sanitized_messages is None:
sanitized_messages = list(messages)
sanitized_messages[index] = {**message, "content": content}

return sanitized_messages if sanitized_messages is not None else messages


zai = ZaiProviderProfile(
name="zai",
aliases=("glm", "z-ai", "z.ai", "zhipu"),
env_vars=("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY"),
Expand Down
56 changes: 56 additions & 0 deletions tests/run_agent/test_run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,42 @@ def test_basic_kwargs(self, agent):
assert kwargs["messages"] is messages
assert kwargs["timeout"] == 1800.0

def test_zai_sanitizes_hermes_agent_in_outgoing_system_prompt_only(self, agent):
agent.provider = "zai"
agent.model = "glm-5.2"
agent.base_url = "https://api.z.ai/api/coding/paas/v4"
messages = [
{
"role": "system",
"content": "You are Hermes Agent. Help debug Hermes Agent.",
},
{"role": "user", "content": "Explain the phrase Hermes Agent."},
]

kwargs = agent._build_api_kwargs(messages)

assert kwargs["messages"] is not messages
assert (
kwargs["messages"][0]["content"]
== "You are Hermes framework. Help debug Hermes framework."
)
assert kwargs["messages"][1]["content"] == "Explain the phrase Hermes Agent."
assert messages[0]["content"] == "You are Hermes Agent. Help debug Hermes Agent."

def test_non_zai_preserves_hermes_agent_system_prompt(self, agent):
agent.provider = "custom"
agent.model = "llama-3.3"
agent.base_url = "https://custom.example.test/v1"
messages = [
{"role": "system", "content": "You are Hermes Agent."},
{"role": "user", "content": "hi"},
]

kwargs = agent._build_api_kwargs(messages)

assert kwargs["messages"] is messages
assert kwargs["messages"][0]["content"] == "You are Hermes Agent."

def test_public_moonshot_kimi_k2_5_omits_temperature(self, agent):
"""Kimi models should NOT have client-side temperature overrides.

Expand Down Expand Up @@ -3262,6 +3298,26 @@ def test_summary_strips_strict_schema_foreign_fields(self, agent):
assert messages[2]["tool_name"] == "execute_code"
assert messages[1]["codex_reasoning_items"] == [{"id": "rs_1"}]

def test_zai_summary_sanitizes_cached_system_prompt_without_mutating_cache(self, agent):
agent.provider = "zai"
agent.model = "glm-5.2"
agent.base_url = "https://api.z.ai/api/coding/paas/v4"
agent._base_url_lower = agent.base_url.lower()
agent.client.chat.completions.create.return_value = _mock_response(content="Summary")
agent._cached_system_prompt = "You are Hermes Agent. Debug Hermes Agent."

result = agent._handle_max_iterations(
[{"role": "user", "content": "Explain Hermes Agent."}],
60,
)

assert result == "Summary"
sent_msgs = agent.client.chat.completions.create.call_args.kwargs["messages"]
assert sent_msgs[0]["role"] == "system"
assert sent_msgs[0]["content"] == "You are Hermes framework. Debug Hermes framework."
assert sent_msgs[1]["content"] == "Explain Hermes Agent."
assert agent._cached_system_prompt == "You are Hermes Agent. Debug Hermes Agent."

def test_summary_omits_provider_preferences_for_non_openrouter(self, agent):
agent.base_url = "https://api.openai.com/v1"
agent._base_url_lower = agent.base_url.lower()
Expand Down