Skip to content

MiniMax provider 완성 + 포털 드롭다운 스크롤 수정 - #120

Merged
lidge-jun merged 7 commits into
devfrom
codex/minimax-provider-repair
Aug 4, 2026
Merged

MiniMax provider 완성 + 포털 드롭다운 스크롤 수정#120
lidge-jun merged 7 commits into
devfrom
codex/minimax-provider-repair

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

외부 기여 PR #118(MiniMax image provider)을 받을 수 있는 상태로 만들고, 이슈 #119를 함께 담았다.

담긴 것

PR #118을 그대로 받을 수 없었던 이유

CI를 승인해 돌려보니 전 매트릭스가 첫 스텝에서 실패했다. 그 외에 리뷰에서 나온 것들:

키 검증이 실제로 과금됐다. {model, prompt}만 보내면 나머지가 기본값으로 채워져
정상 생성 요청이 된다. 키를 저장하거나 바꿀 때마다 이미지 한 장 값이 나가고 결과는
버려졌다. MiniMax가 OpenAI 호환 GET /v1/models를 제공하므로 다른 provider와 같은
방식으로 바꿨다. 검증 URL도 region을 따라가게 했다.

판정이 fail-open이었다. 401/1004/2049만 거부해서 403·429·500이나 깨진 JSON이 오면
잘못된 키를 저장했다. 이제 문서화된 목록 형태를 확인했을 때만 통과시킨다.

모델을 몰래 바꿨다. reference를 붙이면 사용자가 고른 image-01image-01-live
바꿔 보내면서 히스토리에는 원래 모델을 적었다. 그 값은 결과 메타데이터로 표시되고
모델 복원에도 쓰인다. 공식 문서상 image-01subject_reference를 지원하니 바꿀
이유가 없다.

웹에서 쓸 수 없었다. ui/src에 MiniMax가 한 글자도 없어서 provider 선택도 키 입력도
불가능했다. UI 빌드가 통과한 건 빠진 union이 기존 타입과 모순되지 않아서다.

그 밖에 타임아웃 오분류(502→504), 문자열 안전 카운터 미인식, 다운로드 크기 상한 부재,
구조 문서 미갱신을 고쳤다.

새로 막은 것

global 리전에서 image-01-live를 참조 없이 쓰는 조합은 공식 허용 범위 밖이다.
모델 강제 전환을 없애면서 이 조합이 도달 가능해져서, 로컬에서 막고 무엇을 하면 되는지
알려준다. 그 안내가 UI에서 "Generation failed"로 뭉개지지 않도록 오류 레지스트리에도
등록했다.

검증

  • 전체 스위트 2083건 통과 (fail 0)
  • 각 수정마다 해당 분기를 실제 발화시키는 테스트를 붙였고, 가드를 되돌리면 그 케이스만
    실패하는 것을 확인했다
  • 실행 중인 UI에서 provider picker·모델 전환·설정 라벨을 관측했다
  • 브랜치 전체 gitleaks 스캔 clean

이슈 #119 수정도 같은 방식으로 확인했다: 수정 전 번들에서 버그를 재현하고, 수정 후
내부 스크롤에서 메뉴가 유지되며 외부 스크롤에서는 닫히는 것을 관측했다.

Closes #119

lidge-jun and others added 6 commits August 4, 2026 11:16
포털로 띄운 Select는 위치가 fixed라서 트리거가 움직이면 패널이 떨어져 나온다.
그래서 캡처 단계로 window scroll을 듣고 닫아 왔는데, 캡처는 window -> document ->
target 순으로 내려가므로 목록 자신이 낸 스크롤도 같은 핸들러에 도달했다. 목록은
overflow-y: auto인 스크롤 컨테이너라, 사용자가 옵션을 훑으려고 휠을 굴리는 순간
메뉴가 닫혔다.

이벤트 타깃이 목록 안쪽이면 닫지 않도록 판정을 분리했다. 판정을 ui/src/lib/
portalDismiss.ts의 순수 함수로 빼서 경계 조건(내부/자손/외부/document/window/
ref 없음)을 테스트로 고정했다. resize는 항상 위치를 무효화하므로 그대로 닫는다.

포털을 쓰는 6개 호출 지점(사이드바 provider/model, 프롬프트 빌더 모델 메뉴,
설정 provider 상태, MCP 레퍼런스 슬롯 2곳)이 함께 해결된다.

실측 확인 (agbrowse, 격리 서버):
- 수정 전: 내부 스크롤 -> aria-expanded="false", 목록 언마운트 (버그 재현)
- 수정 후: 내부 스크롤 -> aria-expanded="true", scrollTop=60 (유지)
- 수정 후: 외부 스크롤 -> aria-expanded="false" (#79 회귀 없음)
- npm test 2050건 통과 (fail 0)

mcp-provider-ui-contract는 옛 무가드 배선을 정규식으로 고정하고 있어 함께 갱신했다.
계약의 의도는 "스크롤 시 닫힌다"이지 "무가드여야 한다"가 아니다.
Wire a direct MiniMax image-generation endpoint adapter alongside the existing
direct OpenAI Responses and Atlas Cloud paths. The adapter calls
POST /v1/image_generation with a Bearer API key, supports image-01 /
image-01-live, maps attached reference images to the subject_reference field
for image-to-image, and parses both url and base64 response formats.

Region selects the global (api.minimax.io/v1) or China (api.minimaxi.com/v1)
base URL. The minimax lane is registered through providerOptions, the model
catalog, capabilities, key management, the classic/multimode/node/edit/agent
pipelines, and the CLI lane resolver. Web search, reasoning, mask, and video
controls are ignored on this provider; image-to-image supports at most one
subject reference.

Adds minimax model validation, runtime-context key fields, env/config wiring,
a provider contract test, and updates the affected lane/provider contract
tests.
MiniMax provider(#118)를 받기 전에 고쳐야 할 결함들이다.

키 검증이 실제로 이미지를 생성했다. `{model, prompt}`만 보내면 나머지가 기본값으로
채워져서 정상 생성 요청이 되고, 키를 저장하거나 바꿀 때마다 이미지 한 장 값이
나간다. 결과는 그대로 버려진다. MiniMax가 OpenAI 호환 `GET /v1/models`를 제공하므로
다른 provider와 같은 방식으로 바꿨다. 무효 키에 401/1004가 오는 것을 global·CN
양쪽에서 확인했다. 검증 URL도 region을 따라가게 했다 — 전에는 중국 리전 설정에서도
global 호스트를 때렸다.

판정도 뒤집었다. 예전엔 401/1004/2049만 거부해서 403·429·500이나 깨진 JSON이
오면 잘못된 키를 그냥 저장했다. 이제 문서화된 목록 형태를 확인했을 때만 통과시킨다.
MiniMax는 200 안에 오류를 담아 보내기도 해서 `base_resp.status_code`까지 본다.

reference를 붙이면 사용자가 고른 image-01을 image-01-live로 몰래 바꿔 보내면서
히스토리에는 원래 모델을 적었다. 공식 문서상 image-01도 subject_reference를
지원하니 바꿀 이유가 없다. 대신 global 리전에서 image-01-live를 참조 없이 쓰는
조합은 공식 허용 범위 밖이라 로컬에서 막고, 무엇을 하면 되는지 알려준다.

그 밖에:
- `AbortSignal.timeout()`은 TimeoutError를 던지는데 AbortError만 보고 있어서
  타임아웃이 네트워크 실패(502)로 새어 나갔다. 504로 잡는다.
- 안전 차단 카운터를 숫자로만 읽어서, 공식 응답처럼 문자열로 오면 차단을
  upstream 장애로 잘못 보고했다.
- 결과 다운로드에 크기 제한이 없었다. grok 쪽 선례대로 50MB 스트리밍 상한을 두고,
  content-type을 믿는 대신 매직 바이트로 실제 이미지인지 확인한다. 인라인 base64
  경로도 같은 검사를 거친다 — downstream이 미상 MIME을 PNG로 저장하기 때문에
  HTML 오류 페이지가 깨진 .png로 남을 수 있었다.

검증: 어댑터 계약 19건, 키 검증 route 8건 통과. 가드를 되돌리는 ablation에서
해당 케이스만 실패하는 것을 확인했다.
PR #118은 서버만 붙이고 ui/src에는 손대지 않아서, 웹에서는 provider를 고를 수도
키를 넣을 수도 없었다. UI 빌드가 통과한 건 빠진 union이 기존 타입과 모순되지
않아서지 구현됐다는 뜻이 아니었다.

기존 provider(Atlas/Gemini/Grok)가 등록된 자리에 같은 모양으로 넣었다. 새 시각
요소나 레이아웃 변경은 없다.

- provider union, 모델 카탈로그, provider picker, 키 상태, 사용 가능 여부,
  API 키 입력, persistence allowlist, 메타데이터·홈 라벨, provider 상태 목록
- 모델 목록이 provider를 따라가고, 반대로 MiniMax 모델을 고르면 provider가
  따라오도록 store 보정 분기를 맞췄다
- MiniMax는 subject_reference를 하나만 받으므로 composer 상한을 1로 둔다.
  생성 시점 400 대신 붙일 때 막는 게 이 파일의 역할이다
- image-01/image-01-live가 GPT 기본 모델 목록에 새지 않도록 필터에서 제외

MINIMAX_MODEL_REQUIRES_REFERENCE를 오류 레지스트리에 등록했다. 어댑터가 "참조를
붙이거나 image-01로 바꾸라"고 알려줘도, 코드가 등록돼 있지 않으면 UNKNOWN으로
접혀서 "Generation failed"만 뜬다.

legacy edit CLI는 provider만 광고하고 모델 allowlist를 갱신하지 않아 실행이
거부됐다. 두 모델을 추가했다.

실측: provider picker에 MiniMax 노출, 선택 시 모델이 image-01로 보정, 모델 목록이
minimax/minimax live로 전환, 설정에 키 입력과 라벨 렌더. CLI는 모델 거부가 사라졌다.
계약 테스트로 F9 오류의 toast 해석 경로와 1-reference 상한을 고정했다.
CI가 첫 스텝에서 이 계약으로 fast-fail한다. 모든 코드 변경이 끝난 뒤
한 번에 갱신했다. 생성물이므로 손으로 고치지 않고
npm run docs:refresh-line-counts로 재생성했다.
푸시 직전 감사에서 나온 경계 결함이다.

키 검증에서 `base_resp.status_code`를 숫자일 때만 검사해서, 문자열 "1008"처럼
타입이 흔들린 오류 응답이 오면 모델 배열만 있으면 그대로 통과했다. 앞선 커밋에서
fail-open을 걷어냈는데 타입 하나가 빠져나간 셈이다. 이제 status_code가 있으면
숫자 0으로 해석될 때만 통과시킨다.

인라인 base64 응답에는 크기 상한이 없었다. URL 다운로드만 50MB로 막아 뒀는데,
MiniMax가 response_format을 무시하고 큰 payload를 인라인으로 돌려주면 그대로
메모리에 올라간다. base64 길이로 디코딩 전에 상한을 건다.

회귀 테스트: 문자열 status_code와 해석 불가 status_code에서 키가 저장되지 않는 것,
과대 인라인 payload가 거부되는 것을 고정했다. 가드를 되돌리면 정확히 그 케이스만
실패하는 것을 확인했다.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6fe2e1c9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +191 to +195
export async function generateViaMinimax(
prompt: string,
ctx: RuntimeContext,
options: MinimaxGenerateOptions = {},
): Promise<MinimaxImageResult> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Split the oversized MiniMax generation function

generateViaMinimax spans lines 191–347, substantially exceeding the repository's 50-line function limit and combining validation, request construction, transport, response parsing, downloading, logging, and error translation. Extract these responsibilities into focused helpers so the new provider remains maintainable and conforms to the project convention.

AGENTS.md reference: AGENTS.md:L85-L85

Useful? React with 👍 / 👎.

Comment on lines +108 to +111
async function downloadMinimaxImage(
url: string,
signal: AbortSignal,
): Promise<{ b64: string; mime: string }> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add error handling to the async download helper

downloadMinimaxImage performs URL parsing, network I/O, stream reads, and cancellation without its own try/catch, contrary to the repository requirement for every async operation. Although its current caller wraps it, helper-local handling is required to preserve normalized MiniMax errors if it is reused or its call site changes.

AGENTS.md reference: AGENTS.md:L86-L86

Useful? React with 👍 / 👎.

Comment thread lib/nodeGeneration.ts
Comment on lines +302 to +304
references: parentB64
? [{ b64: parentB64, declaredMime: null, detectedMime: null }, ...((refCheck.refDetails || []) as any[])]
: refCheck.refDetails,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Respect parent-only mode when passing MiniMax references

When a Node request uses contextMode: "parent-only", refsForRequest is intentionally emptied and inputImageCount is calculated from that filtered list, but this MiniMax branch passes refCheck.refDetails instead. A request with a parent plus any composer reference therefore sends both images and fails with MINIMAX_REF_TOO_MANY; without a parent, it still sends a reference that the selected context mode was supposed to ignore. Pass the already-filtered references here.

Useful? React with 👍 / 👎.

CI의 audit 게이트가 fast-uri와 ip-address의 신규 공시 취약점으로 막혔다.
이 PR 이전 origin/dev에서도 똑같이 실패하므로 여기서 생긴 문제는 아니지만,
같은 브랜치에서 CI를 그린으로 만들려면 함께 올려야 한다.

package.json은 건드리지 않았다. 전이 의존성 두 개만 lockfile에서 갱신했다.

확인: npm ci 성공, 네이티브 의존성 smoke 통과, audit 게이트(root/ui) 통과,
install-policy 통과, 전체 테스트 2083건 통과.
@lidge-jun
lidge-jun merged commit 8fe24ed into dev Aug 4, 2026
4 checks passed
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