Skip to content

Consume the typed RendererConfig surface#1467

Merged
hallerite merged 7 commits into
mainfrom
feat/typed-renderer-config
May 26, 2026
Merged

Consume the typed RendererConfig surface#1467
hallerite merged 7 commits into
mainfrom
feat/typed-renderer-config

Conversation

@hallerite
Copy link
Copy Markdown
Member

@hallerite hallerite commented May 25, 2026

Summary

Switches ClientConfig and RendererClient to consume the typed
renderers.RendererConfig discriminated union (rendered in
PrimeIntellect-ai/renderers#60).

  • Removes the flat renderer, tool_parser, reasoning_parser, preserve_* fields from ClientConfig.
  • Adds a single renderer_config: RendererConfig | None field. Pydantic dispatches on the name discriminator ("qwen3", "glm-5", …) and exposes exactly the kwargs that renderer supports.
  • renderer_model_name / renderer_pool_size stay as separate fields — they're orthogonal to the renderer choice.
  • _get_renderer_or_pool collapses to create_renderer_pool(model, config). Cache key shrinks from a 7-tuple of flat fields to (model, pool_size, config_json).

API

Before:

ClientConfig(
    client_type="renderer",
    renderer="qwen3.5",
    tool_parser=None,
    reasoning_parser=None,
    preserve_all_thinking=True,
)

After:

from renderers import Qwen35RendererConfig

ClientConfig(
    client_type="renderer",
    renderer_config=Qwen35RendererConfig(
        enable_thinking=False,
        add_vision_id=True,
        preserve_all_thinking=True,
    ),
)

Bogus combinations (e.g. add_vision_id under name="qwen3") raise a pydantic.ValidationError at config-load time instead of at renderer construction.

Stacking

Pins renderers to a git SHA on the renderers PR's head while it's in review. When that lands and a new dev release ships, this PR bumps to the PyPI release.

Validation

Static-only at the moment — the renderers branch hasn't shipped to PyPI, so the verifiers venv can't be re-synced to run pytest against the new typed surface yet.

  • uv run ruff check ... — clean
  • Test fixtures in test_renderer_client.py updated to construct typed configs; will run on CI after the renderers dep resolves.

Test plan

  • CI runs once renderers typed-config branch is available (either merged + dev release, or the git pin resolves in the lockfile)
  • Manual: round-trip a sample ClientConfig through TOML → pydantic → _get_renderer_or_pool and confirm the typed config flows through

Note

Medium Risk
Breaking change to renderer ClientConfig shape and shared-pool cache keys; rollout configs must migrate before deploy, and pool reuse behavior may differ for equivalent old flat settings.

Overview
Replaces flat renderer client settings with a typed renderers.RendererConfig object on ClientConfig, and threads that object through RendererClient pool creation.

ClientConfig drops renderer, tool_parser, reasoning_parser, preserve_all_thinking, and preserve_thinking_between_tool_calls in favor of renderer_config: RendererConfig | None (optional import when verifiers[renderers] is not installed). RendererClient now calls create_renderer_pool(model, renderer_config, …) and keys shared pools on (renderer_model_name, pool_size, renderer_config.model_dump_json()) instead of the old seven-field tuple—so pool sharing follows the full serialized config, not separate parser/preserve flags.

Dependency and test alignment: renderers is pinned to >=0.1.8.dev28 (with lockfile updates for fastokens, prime-pydantic-config, and removal of tyro/typeguard transitives). Tests construct typed configs (e.g. Qwen3VLRendererConfig) and use config_from_name() with create_renderer. E2E ScriptedVLLM returns an httpx-like response whose .content is JSON bytes for parse_generate_response.

Migration: configs must use discriminated renderer config objects (e.g. Qwen35RendererConfig(...)) instead of string renderer plus loose kwargs; invalid field combinations fail at Pydantic validation time.

Reviewed by Cursor Bugbot for commit fa4652a. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Replace renderer string fields with typed RendererConfig in ClientConfig

  • Replaces the renderer: str, tool_parser, reasoning_parser, preserve_all_thinking, and preserve_thinking_between_tool_calls fields in ClientConfig with a single renderer_config: RendererConfig | None field from the renderers package.
  • Updates RendererClient._get_renderer_or_pool to pass the typed config object directly to create_renderer_pool, keying the shared pool cache on (renderer_model_name, pool_size, renderer_config_json) instead of the previous 7-tuple.
  • Updates test utilities and e2e helpers to call config_from_name(renderer_name) when constructing renderers from a string name.
  • Behavioral Change: requests that previously shared a renderer pool based on matching parser/preserve flags will now share pools based on the serialized renderer_config JSON, which may change pool-sharing behavior for existing configurations.

Changes since #1467 opened

  • Made renderers package an optional dependency for verifiers.types module by conditionally importing RendererConfig [f322bc9]
  • Changed renderers dependency specification from Git VCS URL to PyPI version constraint in optional dependency groups [1a406bf]
  • Reformatted configuration key assignment in RendererClient [6c6af70]
  • Updated dependency lock file [6c6af70]
  • Updated renderers package dependency constraint to version >=0.1.8.dev28 [d107442]
  • Introduced _ScriptedResponse class and modified ScriptedVLLM.post method to return response objects [fa4652a]

Macroscope summarized 5727298.

Replaces the flat ``renderer``, ``tool_parser``, ``reasoning_parser``,
``preserve_*`` fields on ``ClientConfig`` with a single typed
``renderer_config: RendererConfig | None`` field. The discriminated union
exposes exactly the kwargs each renderer's chat template honours, so a
TOML / Python user picks ``Qwen35RendererConfig(enable_thinking=False)``
instead of feeding flat strings into a runtime-validated allowlist.

``renderer_model_name`` / ``renderer_pool_size`` stay as separate fields:
they're orthogonal to the renderer choice itself.

``RendererClient._get_renderer_or_pool`` simplifies to passing the typed
config straight through to ``create_renderer_pool(model, config)``. Cache
key collapses from a 7-tuple of flat fields to ``(model, pool_size,
config_json)``; the typed config is frozen and hashable but we serialise
it for a deterministic key shape that's stable across pydantic version
bumps.

Pins ``renderers`` to a git SHA on ``feat/chat-template-kwargs-renderers``
until that PR merges and a dev release ships.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread verifiers/types.py Outdated
Comment thread pyproject.toml Outdated
Comment thread verifiers/types.py
@macroscopeapp
Copy link
Copy Markdown

macroscopeapp Bot commented May 25, 2026

Approvability

Verdict: Needs human review

This PR makes a breaking API change to ClientConfig, removing 5 fields (renderer, tool_parser, reasoning_parser, preserve_all_thinking, preserve_thinking_between_tool_calls) and replacing them with a typed renderer_config from an external dependency. Breaking interface changes warrant human review.

You can customize Macroscope's approvability policy. Learn more.

The typed-config refactor landed on renderers main as ``c86c50b``
(tag ``renderers-v0.1.8.dev27``). Switching the git URL pin from the
pre-merge feature-branch SHA to the dev tag — same commit, more
readable, and stable to anyone reading the diff after the feature
branch is deleted. PyPI dev release for the same version is pending
a publisher-config fix; once it lands this can collapse to a plain
``renderers>=0.1.8.dev27`` PyPI pin.
Comment thread pyproject.toml Outdated
Comment thread verifiers/types.py Outdated
hallerite and others added 2 commits May 25, 2026 22:50
``from renderers import RendererConfig`` at the top of types.py made
``import verifiers`` fail with ``ModuleNotFoundError`` whenever the
optional ``[renderers]`` extra wasn't installed, because
``verifiers/__init__.py`` eagerly runs ``from .types import *``.

Move the import under ``TYPE_CHECKING`` for static analysis and
try-import at runtime with an ``Any`` fallback. Pydantic still
validates the discriminated union when renderers is present; when it
isn't, the field accepts anything (the default ``None`` is what
non-renderer clients use, and the renderer client only loads when
renderers is installed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the git-URL pin (introduced when the dev release was waiting
on PyPI trusted-publisher config) with a regular PyPI dependency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1a406bf. Configure here.

Comment thread pyproject.toml Outdated
hallerite and others added 3 commits May 25, 2026 23:31
``uv lock`` after switching from the git-URL renderers pin to PyPI
(``renderers>=0.1.8.dev27``). Also reformats one line in
``renderer_client.py`` that drifted past ruff's line length.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dev28 (renderers#64) lazy-loads concrete renderer classes via PEP 562,
so consumers of just the typed ``RendererConfig`` discriminated union
don't pull ``transformers`` into ``sys.modules``.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
``renderers/client.py`` switched to parsing the raw response bytes
(``raw_response.content``) when routed-experts sidecar support landed
(renderers PR #21 / 3177399). The e2e test's ScriptedVLLM still
returned the parsed dict directly, so ``parse_generate_response`` hit
``AttributeError: 'dict' object has no attribute 'content'`` on every
rollout and the trajectory came back empty.

Wrap the canned payload in ``_ScriptedResponse`` whose ``.content`` is
the JSON-encoded bytes, matching what ``httpx.AsyncClient.post``
actually returns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hallerite hallerite merged commit 6b2155e into main May 26, 2026
15 checks passed
@hallerite hallerite deleted the feat/typed-renderer-config branch May 26, 2026 00:24
snimu added a commit that referenced this pull request May 26, 2026
…ass-through-info-2026-05-19

Resolved conflicts in pyproject.toml, tests/test_renderer_e2e.py, and
uv.lock by taking main's side: renderers >=0.1.8.dev28 and the typed
RendererConfig surface from #1467 (config_from_name(...), _ScriptedResponse).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants