Skip to content

fix(zai): configure strict concurrency gate via config - #64911

Open
Da7-Tech wants to merge 1 commit into
NousResearch:mainfrom
Da7-Tech:fix/zai-concurrency-config
Open

Da7-Tech wants to merge 1 commit into
NousResearch:mainfrom
Da7-Tech:fix/zai-concurrency-config

Conversation

@Da7-Tech

@Da7-Tech Da7-Tech commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds one strict process-local concurrency gate for Z.AI requests made by the direct agent path and Mixture-of-Agents reference/aggregator calls.

The gate is profile-configurable, interruptible while queued, and held for the full lifetime of streaming calls. It prevents Z.AI fan-out from bypassing the configured cap or leaking another caller's semaphore permit.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Tests

Changes Made

  • Added agent/zai_concurrency.py, backed by a process-wide bounded semaphore.
  • Added profile settings under providers.zai:
    • max_concurrent defaults to 2; 0 disables the gate.
    • acquire_timeout_seconds defaults to 0, meaning wait until a slot or interruption.
  • Applied the same gate to:
    • direct agent requests;
    • MoA reference calls;
    • MoA aggregator calls;
    • MoA streaming calls for the iterator's full lifetime.
  • Propagated the live agent interruption callback through:
    • initial MoA construction;
    • one-shot MoA context aggregation;
    • runtime model switching to an MoA preset.
  • Made slot acquisition ownership-safe across timeout, exception, and interrupt races.
  • Detects Z.AI host-first from the parsed base-URL hostname (the provider name is a fallback only when no host parses), without false matches from URL paths, query strings, or lookalike domains, and with protocol-relative, schemeless, and opaque scheme:host URL forms all resolving to their real host.
  • Rejects non-integral concurrency values and non-finite timeouts by falling back to safe defaults.
  • Added configuration and provider documentation.

How to Test

Focused coverage:

./scripts/run_tests.sh \
  tests/agent/test_zai_concurrency.py \
  tests/agent/test_moa_zai_concurrency.py \
  -q

Static and cross-platform checks:

uvx ruff check \
  agent/zai_concurrency.py \
  agent/agent_init.py \
  agent/agent_runtime_helpers.py \
  agent/conversation_loop.py \
  agent/moa_loop.py \
  hermes_cli/config.py \
  tests/agent/test_zai_concurrency.py \
  tests/agent/test_moa_zai_concurrency.py

python3 -m py_compile \
  agent/zai_concurrency.py \
  agent/agent_init.py \
  agent/agent_runtime_helpers.py \
  agent/conversation_loop.py \
  agent/moa_loop.py \
  hermes_cli/config.py

python3 scripts/check-windows-footguns.py --diff origin/main
git diff --check

Verification Results

Three independent verification methods passed, with the final branch head at 81c74b3af:

  1. Repository tests: 53 focused Z.AI/MoA tests passed on the final head, including adversarial URL-grammar and detection-override regressions. During the final audit, a broader run across 41 Z.AI, MoA, configuration, provider, and model-switch files passed 942 tests. After later upstream movement touched MoA context propagation, the branch was rebased and 52 directly affected Z.AI/MoA/portal-context tests passed before the final focused rerun.
  2. Static and ownership audit: Ruff, Python compilation, the Windows footgun checker, and whitespace validation passed. Acquire/release, timeout, interrupt-race, stream-close, and model-switch paths were manually traced.
  3. Independent runtime stress: 200 concurrent threads completed with zero stranded threads and an observed peak of exactly two in-flight Z.AI calls.

The review-specific regression holds the sole Z.AI permit, switches a live agent to MoA, confirms the new client queues without calling the provider, flips _interrupt_requested, receives InterruptedError, and proves the foreign permit was not released.

Scope and Limitations

  • The cap is process-local. Multiple Hermes processes sharing one Z.AI key each have an independent gate.
  • Configuration is read from the active profile's config.yaml when the module is loaded.
  • Detection is host-first: a call is gated only when its resolved base-URL host is Z.AI, so a zai/glm provider pointed at a local or third-party endpoint via GLM_BASE_URL is not throttled. The provider name is used only when no host can be parsed.
  • The gate covers the direct-agent and MoA request paths. Auxiliary side-model calls (vision, web-extract, approval, compression, and other auxiliary-configured tasks) are not routed through it, so a Z.AI-backed auxiliary configuration can add in-flight requests beyond max_concurrent.
  • This PR does not alter provider quota accounting or remote rolling usage limits.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have documented the configuration and operational limitations
  • My changes generate no new warnings
  • I have added regression and adversarial tests
  • Focused and expanded tests pass locally

@Da7-Tech
Da7-Tech force-pushed the fix/zai-concurrency-config branch from 1e217a5 to 11801ac Compare July 15, 2026 10:41
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard provider/zai ZAI provider area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists labels Jul 15, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for re-scoping the prior Z.AI concurrency work to profile-safe config.yaml; that matches the direction recorded on #55037. The premise is still present on current main: agent/conversation_loop.py:1351-1362 sends direct calls without a Z.AI gate, while agent/moa_loop.py:407-428 fans references out concurrently.

Problems

  • agent/agent_init.py:895 passes the new interrupt callback only for initial construction. agent/agent_runtime_helpers.py:1915-1934 reconstructs MoAClient during switch_model() without interrupt_check. After switching to an MoA preset, a saturated zero-timeout Z.AI gate cannot observe agent._interrupt_requested, contrary to the PR's interruptible-wait contract.

Suggested changes

  • Pass the same callback in agent_runtime_helpers.py:1934, and add a switch-to-MoA regression test that verifies a queued Z.AI slot wait interrupts.

Automated hermes-sweeper review.

Comment thread agent/agent_init.py
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
@Da7-Tech
Da7-Tech force-pushed the fix/zai-concurrency-config branch from 11801ac to 98f01c8 Compare July 16, 2026 06:04
@Da7-Tech

Copy link
Copy Markdown
Contributor Author

Addressed in 98f01c80e.

  • switch_model() now reconstructs MoAClient with the same _interrupt_requested callback used during initial agent construction.
  • Added a switch-path regression that holds the only Z.AI slot, confirms the switched client queues, flips the live interrupt flag, receives InterruptedError before call_llm, and verifies the foreign slot was not released.

Validation completed:

  • 84 related Z.AI, MoA, and model-switch tests passed.
  • ruff, py_compile, the Windows-footgun checker, and git diff --check passed.
  • Independent 100-thread stress completed all 100 calls with an observed peak of exactly 2 and no live threads.

The branch is rebased on current main (2ea39daeb) and GitHub reports it mergeable.

@Da7-Tech
Da7-Tech force-pushed the fix/zai-concurrency-config branch 2 times, most recently from fb1c144 to 8bf1fd5 Compare July 16, 2026 08:19
@Da7-Tech

Da7-Tech commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Final follow-up is complete in 8bf1fd561. In addition to the review-requested switch_model() interrupt callback and regression, the gate now uses parsed hostname matching instead of whole-URL substring matching, rejects fractional concurrency caps and non-finite timeouts, and includes adversarial coverage for lookalike hosts and invalid values.

The branch was rebased through current upstream movement and the PR description now matches the actual diff. Final evidence includes 37 focused tests on the final head, a 942-test related audit, 52 directly affected Z.AI/MoA/portal-context tests after the later MoA upstream change, Ruff, Python compilation, Windows-footgun and whitespace checks, a 200-thread peak-cap stress test, and a privacy scan.

Add a process-local semaphore for resolved Z.AI calls across the direct
agent and MoA references and aggregators. Streaming calls retain their slot
for the iterator lifetime, and interactive waits remain interruptible.

Read max_concurrent and acquire_timeout_seconds from providers.zai in the
active profile config. Zero disables the gate; saturation waits or raises
locally and never bypasses the cap. No user-facing environment controls are
added.

Cover direct runtime wiring, MoA fan-out and streaming, profile-scoped
config-set behavior, timeouts, interrupts, and strict concurrency bounds.

Detection is host-first: a resolvable base-URL host is authoritative and the
provider name is consulted only when no host parses, so a zai/glm provider
whose GLM_BASE_URL is overridden to a local or third-party endpoint is not
throttled while real Z.AI destinations stay gated. The URL grammar covers
protocol-relative (//host), schemeless URLs whose path or query embeds
'://', and opaque 'https:host' forms; the name fallback strips whitespace.
Regression tests cover the override, fallback, protocol-relative, and
hostile-URL cases, and the provider guide documents host-first detection and
that auxiliary side-model calls are not gated.
@Da7-Tech
Da7-Tech force-pushed the fix/zai-concurrency-config branch from 4fc46a3 to 81c74b3 Compare July 16, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists provider/zai ZAI provider sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants