Skip to content

fix(opencode): export per-model attachment and modalities capabilities - #4291

Closed
L4XB wants to merge 1 commit into
lidge-jun:devfrom
L4XB:fix/opencode-export-input-modalities
Closed

fix(opencode): export per-model attachment and modalities capabilities#4291
L4XB wants to merge 1 commit into
lidge-jun:devfrom
L4XB:fix/opencode-export-input-modalities

Conversation

@L4XB

@L4XB L4XB commented Sep 11, 2026

Copy link
Copy Markdown

Summary

  • The OpenCode config export emitted only name and limit per model. Because the custom opencodex provider id is absent from models.dev, opencode's model loader defaulted capabilities.attachment and capabilities.input.image to false, so every exported model was cataloged text-only and the client blocked image attachments before a request reached the proxy, including vision-capable native slugs and rows whose /api/models entry declares ["text","image"] ([Bug]: OpenCode config export omits per-model image capabilities — attachments blocked for every opencodex model #4286).
  • inputModalities from the /api/models row is now carried into OpencodeCatalogModel (and into hand-assembled routed models for the launch block), and opencodeProviderBlocks() emits attachment plus modalities on both the V1 and V2 entries when a model declares its modalities. Declared values are filtered to what opencode's schema accepts (text, audio, image, video, pdf); if none remain, text is the floor. A model with nothing declared stays capability-free so the client keeps its own default rather than a guessed one, which mirrors the existing "no metadata is guessed" rule for limit. V1 and V2 own separate modalities objects, like limit.
  • exportModelsFromProxyRows reads the modalities from the catalog entry instead of re-joining them by namespaced, so the visible, deduped row is the single source for modalities as it already is for the effort ladder.
  • Exported entry for the issue's example becomes {"name": "gpt-5.6-luna (native)", "limit": {...}, "attachment": true, "modalities": {"input": ["text","image"], "output": ["text"]}}.

Closes #4286 (to be closed by hand once this is on dev, per the branch policy).

Verification

  • bun x tsc --noEmit: clean.
  • bun test tests/config/client-config-export.test.ts tests/providers/opencode-cli.test.ts tests/cli/cli-export-command.test.ts: 152 pass, 0 fail. New tests cover: image-capable, text-only, audio-only, unknown-only and undeclared rows on both blocks; separate modalities objects per block; opencodeCatalogFromProxyRows carrying inputModalities (empty arrays dropped); hand-assembled routed models through buildOpencodeProviderBlock. The existing Fast-expansion test now expects the capability fields for its image-capable fixture.
  • bun test tests/config tests/providers tests/cli tests/clients: 5652 pass / 30 fail on this branch, and the same 30 failures on current origin/dev in this environment (Kiro per-account quota and similar network-dependent cases); no failure is unique to the branch.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (No user docs describe the per-model entry fields; nothing to update.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (Capability metadata only; admission and keys untouched.)

Summary by CodeRabbit

  • New Features

    • OpenCode exports now preserve model input modality information.
    • Provider configurations automatically reflect supported modalities, including text, image, and audio inputs.
    • Attachment support is enabled for models with recognized non-text input capabilities.
    • Modality data is consistently carried through model catalogs and generated provider configurations.
  • Tests

    • Added coverage for modality mapping across OpenCode V1 and V2 configurations, catalog exports, and launch data.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c4a4c8d0-065f-4dea-89eb-6ba1cc859e5f

📥 Commits

Reviewing files that changed from the base of the PR and between 29d632f and 30ce6f8.

📒 Files selected for processing (6)
  • src/cli/export-command.ts
  • src/cli/opencode.ts
  • src/clients/config-export.ts
  • src/clients/config-export/contracts.ts
  • tests/config/client-config-export.test.ts
  • tests/providers/opencode-cli.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

OpenCode model catalog entries now preserve declared input modalities. Config export maps recognized modalities to attachment and modalities fields in both V1 and V2 provider blocks. Tests cover catalog propagation, fallback behavior, and capability serialization.

Changes

OpenCode modality propagation

Layer / File(s) Summary
Catalog modality propagation
src/cli/opencode.ts, src/cli/export-command.ts, src/clients/config-export/contracts.ts
Routed models and proxy rows preserve non-empty inputModalities values in catalog entries. Export mapping reads modalities from the filtered catalog row.
Capability field serialization
src/clients/config-export.ts
Recognized modalities map to attachment and modalities. Unknown-only declarations fall back to text input. V1 and V2 entries receive independent modality arrays.
Modality export validation
tests/providers/opencode-cli.test.ts, tests/config/client-config-export.test.ts
Tests cover image, audio, text-only, unknown, undeclared, routed-model, proxy-row, and V1/V2 serialization cases.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant ProxyRows
  participant opencodeCatalogFromProxyRows
  participant opencodeCapabilityFields
  participant opencodeProviderBlocks
  participant OpenCode
  ProxyRows->>opencodeCatalogFromProxyRows: provide inputModalities
  opencodeCatalogFromProxyRows->>opencodeCapabilityFields: preserve catalog modalities
  opencodeCapabilityFields->>opencodeProviderBlocks: derive attachment and modalities
  opencodeProviderBlocks->>OpenCode: emit V1 and V2 model capabilities
Loading

Merge Risk: ⚪ Minimal · up to 30ce6

OpenCode exports now expose supported declared input capabilities without changing the default behavior for models lacking modality declarations.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #4286 requires inputModalities to reach the OpenCode catalog and requires attachment and modalities on both provider model blocks. src/cli/opencode.ts adds inputModalities to `Opencode…
Out of Scope Changes check ✅ Passed The changed files support the #4286 data path. The src/cli/export-command.ts change keeps modalities on the filtered catalog entry instead of rejoining raw rows, which prevents capability metadata f…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 6 files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: exporting per-model OpenCode attachment and modality capabilities. It matches the propagation and serialization changes in the production …
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added the bug Something isn't working label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This PR stays in draft until every box above is ticked.

@github-actions
github-actions Bot marked this pull request as draft September 11, 2026 17:04
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 71 / 80

이 PR은 OpenCode로 내보내는 모델 설정에 이미지(첨부) 능력 표시가 빠져 있던 구멍을 메웁니다. 지금 dev(HEAD 29d632ff2)의 OpencodeModelEntrynamelimit만 있고, opencodeProviderBlocks()도 그 두 필드만 넣습니다. OpenCode 쪽은 opencodex라는 커스텀 provider id를 models.dev에서 못 찾으면 capabilities.attachmentcapabilities.input.image를 둘 다 false로 둡니다. 그래서 /api/models["text","image"]가 있어도, 비전 모델이어도, 클라이언트가 요청을 프록시까지 보내기 전에 "이 모델은 이미지를 못 받는다"고 막아 버립니다. 그게 #4286입니다.

고치는 길은 세 갈래로 이어집니다. 첫째, /api/models 행의 inputModalitiesOpencodeProxyModelRowopencodeCatalogFromProxyRowsOpencodeCatalogModel까지 그대로 태웁니다. 둘째, opencodeCapabilityFields()가 선언된 모달리티만 OpenCode 스키마가 아는 값(text/audio/image/video/pdf)으로 걸러 attachmentmodalities를 만들고, V1·V2 블록 양쪽에 붙입니다. limit처럼 V1/V2가 각자 다른 modalities 객체를 갖도록 복사하는 것도 같은 규칙을 따릅니다. 셋째, exportModelsFromProxyRows는 예전에 namespaced로 모달리티를 다시 붙이던 Map을 치우고, 카탈로그 한 줄에서 노력 사다리와 모달리티를 같이 읽습니다. 숨기거나 비활성인 중복 행이 보이는 행에 모달리티를 빌려 주는 길을 막는 셈입니다.

선언이 없는 모델은 능력 필드를 아예 안 씁니다. limit을 추측하지 않는 규칙과 같습니다. 선언은 있는데 스키마 밖 값만 있으면 text를 바닥으로 두고 attachment: false입니다. 이미지가 있으면 attachment: true입니다. 오디오만 있어도 attachment: true인데, OpenCode의 attachment가 "텍스트 아닌 입력 허용"에 가깝기 때문입니다. 테스트도 이미지/텍스트만/오디오만/이상한 값만/미선언, V1·V2 객체 분리, 런치용 손조립 라우트, opencodeCatalogFromProxyRows 전달까지 잡았습니다. 범위가 #4286에 딱 맞고, types/config 분할 캠페인과도 겹치지 않습니다.

Pi·Gajae는 이미 inputModalitiesForClient로 비슷한 걸 하고, Codex 쪽 이미지 자동 광고는 예전에 #344/#349로 풀렸는데 OpenCode serializer만 비어 있던 상태입니다. 이 PR은 그 빈칸을 채우는 쪽으로 잘 맞습니다.

src/clients/config-export.ts / opencodeCapabilityFields - OpenCode용 허용 목록을 파일 안에 새로 두었습니다. 같은 폴더의 inputModalitiesForClient(지금은 pi|gajae만)와 역할이 겹칩니다. 당장 틀리진 않지만, 나중에 허용 값이 갈라질 여지는 있습니다.

src/clients/config-export.ts / 미선언 모델 - inputModalities가 비어 있으면 필드를 안 씁니다. 그러면 OpenCode 기본값(attachment/image = false)이 그대로라, /api/models에 모달리티가 빠진 비전 행은 머지 후에도 이미지 첨부가 막힐 수 있습니다. 의도된 최소 수정이지만 운영 공백입니다.

#4286 본문 / noVisionModels·비전 사이드카 - 이슈가 적어 둔 "텍스트 전용 + 사이드카" 자동 광고(#344 계열)는 이 PR에 없습니다. 이미지 선언이 있는 행만 고칩니다.

메인테이너의 판단이 필요한 지점

  • CI(enforce-target 등)가 아직 도는 중이고 merge 상태는 MERGEABLE이지만 BLOCKED입니다. 초록이 뜬 뒤 바로 넣을지, 한 번만 더 볼지.
  • 미선언/noVisionModels 행까지 OpenCode에서 사이드카로 이미지를 받게 할 후속 PR을 열지, 아니면 #4286은 이 PR만으로 닫을지.
  • OPENCODE_INPUT_MODALITIESmodel-metadata.ts의 공통 헬퍼로 합칠지, OpenCode만 따로 둘지.

너의 추천
CI가 초록이면 dev에 머지하세요. #4286은 브랜치 정책대로 손으로 닫고, 사이드카/미선언 자동 광고가 필요하면 짧은 follow-up 이슈만 남기면 됩니다. types/config 분할과 무관하니 닫지 말고 넣는 쪽이 맞습니다.

이 댓글은 grok-bot이 작성했습니다

The OpenCode config export emitted only name and limit per model. With the
custom opencodex provider id absent from models.dev, opencode's model
loader defaulted capabilities.attachment and capabilities.input.image to
false, so every exported model was cataloged text-only and the client
blocked image attachments before a request reached the proxy, including
vision-capable native slugs and rows whose /api/models entry declares
["text","image"].

Carry inputModalities from the proxy row into OpencodeCatalogModel (and
hand-assembled routed models) and have opencodeProviderBlocks emit
attachment plus modalities on both the V1 and V2 entries when a model
declares its modalities. Values outside opencode's schema are dropped with
text as the floor; a model with nothing declared stays capability-free so
the client keeps its own default. The export command now reads the
modalities from the catalog entry instead of re-joining them by key.

Refs lidge-jun#4286
@lidge-jun

Copy link
Copy Markdown
Owner

Superseded / landed via maintainer carry #4300 (squash merge ad09340d79bae6db0a949f9a382c7574ecfc32e2 on dev). Closing in favor of that integration; do not land this fork PR.

@lidge-jun lidge-jun closed this Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants