Skip to content

Fix adapter multimodal spawn contract#1928

Merged
chernistry merged 2 commits into
mainfrom
fix/1785-adapter-multimodal-signature
May 22, 2026
Merged

Fix adapter multimodal spawn contract#1928
chernistry merged 2 commits into
mainfrom
fix/1785-adapter-multimodal-signature

Conversation

@chernistry

@chernistry chernistry commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds multimodal_context to concrete adapter spawn() overrides so they match the base adapter contract.
  • Refuses multimodal attachments before process launch for adapters that do not support them.
  • Forwards multimodal context through CachingAdapter and bypasses text-only response cache hits when attachments are present.

Verification

  • uv run pytest tests/unit/test_adapter_consistency.py -q --tb=short
  • uv run pytest tests/unit/test_caching_adapter.py -q --tb=short
  • uv run ruff format --check src/bernstein/adapters src/bernstein/core/agents/multimodal.py tests/unit/test_adapter_consistency.py tests/unit/test_caching_adapter.py
  • uv run ruff check src/bernstein/adapters src/bernstein/core/agents/multimodal.py tests/unit/test_adapter_consistency.py tests/unit/test_caching_adapter.py
  • uv run pyright --project pyrightconfig.strict.json
  • git diff --check

Refs #1785

Summary by CodeRabbit

  • New Features

    • Added multimodal input gating across adapters; non-multimodal adapters will refuse multimodal context.
    • Marked an adapter as multimodal-capable and updated capability detection.
    • Response-cache reuse now bypassed when multimodal context is provided; multimodal context is forwarded when delegating.
  • Tests

    • Added tests verifying multimodal rejection for non-capable adapters and forwarding behavior.
    • Enhanced adapter conformance tests to require an optional multimodal parameter.

Review Change Stack

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @chernistry, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@chernistry chernistry enabled auto-merge (squash) May 22, 2026 16:33
@github-actions

Copy link
Copy Markdown
Contributor

Sonar insights (advisory, no merge-block)

Snapshot of bernstein on the configured Sonar instance:

Metric Value
Coverage 13.5
Code smells 140
Bugs 11
Vulnerabilities 2
Security hotspots 91

Run bernstein doctor sonar locally for the full surface.

This comment is a soft signal. The Sonar scan runs on push to main; the PR check itself never fails on smells.

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 44a5762d-f366-476a-b4f2-33a9db78eadf

📥 Commits

Reviewing files that changed from the base of the PR and between 01d29f0 and 7d9a47d.

📒 Files selected for processing (3)
  • src/bernstein/adapters/conformance.py
  • tests/unit/test_adapter_conformance.py
  • tests/unit/test_adapter_consistency.py

📝 Walkthrough

Walkthrough

Adds an optional multimodal_context kwarg to adapter spawn() signatures, implements CLIAdapter.refuse_multimodal_if_needed() and calls it early in each spawn(), updates scaffold and plugin abstract signature, adjusts caching to bypass virtual-PID cache hits when multimodal input is present, and adds tests.

Changes

Multimodal context gating across adapter layer

Layer / File(s) Summary
Base multimodal refusal method and plugin protocol
src/bernstein/adapters/base.py, src/bernstein/adapters/plugin_sdk.py
Added CLIAdapter.refuse_multimodal_if_needed() which inspects multimodal_context, extracts attachments from inputs, and invokes refuse_when_incapable(); updated PluginAdapter.spawn() abstract signature to include multimodal_context: Any | None = None.
Adapter scaffold template and conformance generation
src/bernstein/adapters/conformance.py
Updated ADAPTER_SCAFFOLD_TEMPLATE to generate spawn() methods with multimodal_context and an early call to refuse_multimodal_if_needed() (docstring updated).
Multimodal capability registration
src/bernstein/core/agents/multimodal.py
Added "antigravity" to _MULTIMODAL_ADAPTERS so is_multimodal_capable() recognizes it as multimodal-capable.
Adapter spawn signatures and pre-spawn gating
src/bernstein/adapters/*
~40+ adapters (examples: aichat.py, aider.py, amp.py, auggie.py, autohand.py, caching_adapter.py, codex.py, ollama.py, etc.) now accept `multimodal_context: Any
Caching adapter special-case
src/bernstein/adapters/caching_adapter.py
CachingAdapter.spawn requires multimodal_context is None to return a virtual cached SpawnResult (pid=0); when multimodal context is provided it delegates to inner_adapter.spawn(..., multimodal_context=...).
Adapter protocol and multimodal refusal tests
tests/unit/test_adapter_consistency.py
Extended protocol assertions to require multimodal_context kw-only parameter; added TestAdapterMultimodalRefusal that constructs a MultiModalContext with an image attachment and asserts non-multimodal adapters raise CapabilityRefusal on spawn(..., multimodal_context=...).
Caching adapter multimodal forwarding test
tests/unit/test_caching_adapter.py
Added test_multimodal_context_delegates_even_on_cache_hit() to verify multimodal context is forwarded to inner adapter when cache would otherwise serve a virtual PID.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

size/l

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding the multimodal_context parameter to the adapter spawn contract across all adapters.
Description check ✅ Passed The description covers the what, why, and how of the changes, and includes comprehensive verification steps, though the template checklist items are not explicitly marked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1785-adapter-multimodal-signature

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Review-bot acknowledgement summary

  • Must-address findings: 1 (1 acknowledged, 0 open)
  • Informational findings: 1

All must-address findings are resolved or acknowledged.

@github-actions

Copy link
Copy Markdown
Contributor

bernstein doctor observe for PR #1928 (fix/1785-adapter-multimodal-signature): ok=1, warn=1, fail=0, error=0, skipped=2

sonar -- WARN (project bernstein)

metric value delta threshold status
coverage_pct 13.5% new 80.0% fail
code_smells 140 new 50 warn
bugs 11 new 0 fail
vulnerabilities 2 new 0 warn
security_hotspots 91 new 0 fail

code-scanning -- OK (22 open alert(s))

metric value delta threshold status
open_alerts 22 new 0 fail
critical_alerts 0 new 0 ok
high_alerts 0 new 0 ok
medium_alerts 0 new - ok
low_alerts 0 new - ok
Skipped backends (credentials not configured)
  • glitchtip: BERNSTEIN_GLITCHTIP_TOKEN not set
  • dt: DTRACK_URL/TOKEN/PROJECT not set

See docs/observability/unified-doctor.md for backend setup notes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/bernstein/adapters/conformance.py (1)

568-579: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Complete scaffold spawn() signature to match the base adapter contract.

At Line 576-Line 579, the template adds multimodal_context but still omits budget_multiplier and system_addendum. Scaffolded adapters from this template can fail at runtime when spawn() is invoked with the full contract kwargs.

Proposed fix
     def spawn(
         self,
         *,
         prompt: str,
         workdir: Path,
         model_config: ModelConfig,
         session_id: str,
         mcp_config: dict[str, Any] | None = None,
         timeout_seconds: int = 1800,
         task_scope: str = "medium",
+        budget_multiplier: float = 1.0,
+        system_addendum: str = "",
         multimodal_context: Any | None = None,
     ) -> SpawnResult:

As per coding guidelines, src/bernstein/adapters/**/*.py: "Implement CLI agent adapters following the base adapter contract defined in base.py and _contract.py".

Also applies to: 593-593

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bernstein/adapters/conformance.py` around lines 568 - 579, The spawn()
signature in the adapter is missing required parameters from the base adapter
contract, causing runtime failures; update the spawn method (the function named
spawn that returns SpawnResult and accepts ModelConfig) to include the missing
keyword args budget_multiplier and system_addendum (and keep existing
multimodal_context), so its signature matches the base contract used by callers;
ensure the implementation accepts and forwards these args (or provides sensible
defaults) wherever spawn is invoked or delegated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/test_adapter_consistency.py`:
- Around line 99-102: The test currently only checks that adapter.spawn has a
multimodal_context parameter; update test_spawn_has_optional_multimodal_context
to also assert that
inspect.signature(adapter.spawn).parameters["multimodal_context"].default is
None (to ensure it’s optional) and optionally assert that the parameter.kind
indicates it is keyword-only if you want that constraint; reference the
adapter.spawn method and the "multimodal_context" parameter when adding these
assertions.
- Around line 172-176: The test currently parametrizes with "_TESTABLE_ADAPTERS"
which supplies shared mutable adapter instances (adapter_name, adapter) and can
leak state; change the parametrize to pass only adapter_name (e.g., param
"adapter_name" with ids from _TESTABLE_ADAPTERS names) and inside the test
function instantiate a fresh adapter per-case (use your adapter
factory/constructor or a helper like create_adapter(adapter_name) or a mapping
that constructs a new instance) so each test gets a new adapter object instead
of reusing the shared "adapter" from _TESTABLE_ADAPTERS.

---

Outside diff comments:
In `@src/bernstein/adapters/conformance.py`:
- Around line 568-579: The spawn() signature in the adapter is missing required
parameters from the base adapter contract, causing runtime failures; update the
spawn method (the function named spawn that returns SpawnResult and accepts
ModelConfig) to include the missing keyword args budget_multiplier and
system_addendum (and keep existing multimodal_context), so its signature matches
the base contract used by callers; ensure the implementation accepts and
forwards these args (or provides sensible defaults) wherever spawn is invoked or
delegated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 63fc31c9-96d2-44d1-81cb-05e92d1c40c5

📥 Commits

Reviewing files that changed from the base of the PR and between 50cb179 and 01d29f0.

📒 Files selected for processing (50)
  • src/bernstein/adapters/aichat.py
  • src/bernstein/adapters/aider.py
  • src/bernstein/adapters/amp.py
  • src/bernstein/adapters/auggie.py
  • src/bernstein/adapters/autohand.py
  • src/bernstein/adapters/base.py
  • src/bernstein/adapters/caching_adapter.py
  • src/bernstein/adapters/charm.py
  • src/bernstein/adapters/cline.py
  • src/bernstein/adapters/clm.py
  • src/bernstein/adapters/cloudflare_agents.py
  • src/bernstein/adapters/codebuff.py
  • src/bernstein/adapters/codex.py
  • src/bernstein/adapters/cody.py
  • src/bernstein/adapters/composio.py
  • src/bernstein/adapters/conformance.py
  • src/bernstein/adapters/continue_dev.py
  • src/bernstein/adapters/copilot.py
  • src/bernstein/adapters/cursor.py
  • src/bernstein/adapters/devin_terminal.py
  • src/bernstein/adapters/droid.py
  • src/bernstein/adapters/forge.py
  • src/bernstein/adapters/generic.py
  • src/bernstein/adapters/goose.py
  • src/bernstein/adapters/gptme.py
  • src/bernstein/adapters/hermes.py
  • src/bernstein/adapters/iac.py
  • src/bernstein/adapters/junie.py
  • src/bernstein/adapters/kilo.py
  • src/bernstein/adapters/kimi.py
  • src/bernstein/adapters/kiro.py
  • src/bernstein/adapters/letta_code.py
  • src/bernstein/adapters/manager.py
  • src/bernstein/adapters/mistral.py
  • src/bernstein/adapters/mock.py
  • src/bernstein/adapters/ollama.py
  • src/bernstein/adapters/open_interpreter.py
  • src/bernstein/adapters/openai_agents.py
  • src/bernstein/adapters/opencode.py
  • src/bernstein/adapters/openhands.py
  • src/bernstein/adapters/pi.py
  • src/bernstein/adapters/plandex.py
  • src/bernstein/adapters/plugin_sdk.py
  • src/bernstein/adapters/q_dev.py
  • src/bernstein/adapters/qwen.py
  • src/bernstein/adapters/ralphex.py
  • src/bernstein/adapters/rovo.py
  • src/bernstein/core/agents/multimodal.py
  • tests/unit/test_adapter_consistency.py
  • tests/unit/test_caching_adapter.py

Comment thread tests/unit/test_adapter_consistency.py
Comment thread tests/unit/test_adapter_consistency.py
@chernistry chernistry merged commit 699630c into main May 22, 2026
93 of 94 checks passed
@chernistry chernistry deleted the fix/1785-adapter-multimodal-signature branch May 22, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant