-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(devin): give every model the reasoning ladder its catalog already names #4490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # 000 — Devin 추론 사다리: 피커 불일치와 Pi 컨트롤 부재 | ||
|
|
||
| - 단위: `260913_model_picker_grouping_and_effort` | ||
| - 세션: `01a0985e-ce1a-7d12-81b9-c2e93a2bce67` (HOTL, cxc-loop) | ||
| - 기준: `origin/dev` `f7d9dbad03` (#4484 devin-cli→devin 병합 반영) | ||
|
|
||
| ## 사용자가 말한 세 가지 | ||
|
|
||
| | # | 증상 | 판정 | | ||
| |---|---|---| | ||
| | 1 | "gemini 처리했던거처럼 묶는 기능"이 필요하다 | **부분 NOOP** — 접기는 이미 있음, 짝이 빠졌음 | | ||
| | 2 | 추론 매칭 | **실결함** | | ||
| | 3 | Pi 커넥터로 연결하면 추론 피커가 안 보임 | **실결함** | | ||
|
|
||
| 셋 다 뿌리가 하나다. `devin` 레지스트리 행에 `modelReasoningEfforts`가 없다. | ||
|
|
||
| ## Gemini 선례는 두 부분이다 | ||
|
|
||
| 공용 family-grouper 같은 건 없다. 사용자가 기억한 "gemini 처리"는 Antigravity 전용 | ||
| 구현이고(도입 `c07f2d63dc`, 회귀 복구 `06f8e7a944`), 두 조각이 짝을 이룬다. | ||
|
|
||
| | 조각 | Antigravity | Devin 현재 | | ||
| |---|---|---| | ||
| | A. wire variant를 base 한 줄로 접기 | `pickerModelIdForDiscoveredWireId` + `collapsesIntoKnownPickerModel` (`antigravity-models.ts:140-156`) | **있음** — `collapseDevinModelUid` (`live-models.ts:80-86`), `provider-fetch.ts:1706-1750` | | ||
| | B. 접힌 base에 effort 사다리 붙이기 | `ANTIGRAVITY_MODEL_EFFORTS` → `modelReasoningEfforts` (`registry.ts:2139`) | **없음** (`registry.ts:1340-1358`) | | ||
|
|
||
| `antigravity-models.ts:148-150` 주석이 왜 짝이어야 하는지 직접 말한다. | ||
|
|
||
| > `gemini-3.7-flash-high` looks like a model id and is not one: it is the "high" | ||
| > rung of `gemini-3.7-flash`, whose ladder lives in ANTIGRAVITY_MODEL_EFFORTS. | ||
| > Publishing it as its own row is what breaks effort selection. | ||
|
|
||
| Devin은 A만 하고 B를 안 해서, 행은 하나로 접혔는데 그 행에 붙는 사다리가 Devin 것이 | ||
| 아니다. 그래서 "묶는 기능이 필요하다"는 체감이 나온다 — 접기는 됐지만 쓸모가 없다. | ||
|
|
||
| ## 증상 2 — 추론 매칭 불일치 | ||
|
|
||
| `modelReasoningEfforts`가 없으면 `applyReasoningLevels`(`effort.ts:231`)가 기본 | ||
| 사다리로 떨어진다. | ||
|
|
||
| | | 광고되는 사다리 | 실제 레인 | | ||
| |---|---|---| | ||
| | SWE-2 | low, medium, high, xhigh, max, ultra | **medium, high, max** (`devin.ts:116-126` `SWE2_EFFORT`) | | ||
|
|
||
| `low`를 고르면 조용히 medium으로 올라가고, `xhigh`/`ultra`도 max로 접힌다. 컨트롤이 | ||
| 말한 대로 동작하지 않는다. Anthropic 행 주석(`registry.ts:418-420`)이 정확히 이 기준을 | ||
| 세워 뒀다 — "advertising it would offer a control that does not do what it says". | ||
|
|
||
| ## 증상 3 — Pi에서 컨트롤이 아예 안 그려짐 | ||
|
|
||
| omp에 프로바이더 화이트리스트는 없다. `ompEfforts()`가 `ExportModel.reasoningEfforts` | ||
| (`contracts.ts:76`)를 걸러서 **비어 있지 않을 때만** `reasoning: true` + | ||
| `thinking: { mode: "effort", efforts }`를 쓴다(`omp.ts:72`). Devin은 그 배열이 비어 | ||
| 있으니 컨트롤이 통째로 빠진다. | ||
|
|
||
| 이건 Pi만의 문제가 아니다. 같은 필드를 읽는 `pi`, `aside`, `prime`, `omo`, `zcode`, | ||
| `mcode`, `dsh`, `raycast`, OpenCode 계열이 전부 같이 깨진다. `cline` export는 effort | ||
| 필드 자체가 없어 대상이 아니다. | ||
|
|
||
| 선례 커밋은 `df416a439c` (#3454, Anthropic)다. 어댑터는 원래 effort를 보내고 있었는데 | ||
| 레지스트리가 사다리를 안 실어서 Pi형 피커가 숨었던, 글자 그대로 같은 결함이다. | ||
|
|
||
| ## 스크린샷에 대해 | ||
|
|
||
| 사용자 스크린샷의 `swe-2 (devin-cli)` 행은 Codex app composer이고 출처는 | ||
| `model_catalog_json`(`inject.ts:864`)이다. #4484가 `devin-cli` 행을 지웠으므로 그 라벨은 | ||
| 현재 dev에 없다 — 스크린샷은 병합 전 상태이거나 재기동 전 캐시다. 이 단위는 그 라벨을 | ||
| 쫓지 않고 사다리만 고친다. | ||
|
|
||
| ## 작업 단계 | ||
|
|
||
| | wp | 문서 | 산출물 | | ||
| |---|---|---| | ||
| | wp0 | 이 문서 + 010 | 로드맵 | | ||
| | wp1 | (이 문서에 통합) | 선례 확정 — 완료 | | ||
| | wp2 | `010_devin_effort_ladder.md` | 레지스트리 사다리 + 테스트 → PR → merge | | ||
| | wp3 | 통합 | Pi 익스포트는 같은 변경으로 해결 | | ||
|
|
||
| wp2와 wp3은 같은 한 줄짜리 원인을 공유하므로 PR 하나로 착지한다. 나누면 두 번째 | ||
| PR이 빈 변경이 된다. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # 010 — wp2: Devin 모델별 추론 사다리 | ||
|
|
||
| ## NEW: src/providers/devin-models.ts 에 사다리 추가 — 또는 live-models.ts 확장 | ||
|
|
||
| Antigravity는 `ANTIGRAVITY_MODEL_EFFORTS`를 `antigravity-models.ts`에 두고 레지스트리가 | ||
| import한다. Devin도 같은 자리에 둔다. `src/adapters/devin/live-models.ts`가 이미 | ||
| `DEVIN_MODEL_CONTEXT_WINDOWS`를 export하고 레지스트리가 그걸 쓰므로 같은 파일에 붙인다. | ||
|
|
||
| ```ts | ||
| /** | ||
| * Effort ladders per collapsed base model. | ||
| * | ||
| * Cognition spells effort as a model-id suffix, so the picker row that | ||
| * collapseDevinModelUid() produces needs its ladder declared here or the catalog | ||
| * falls back to the six-rung default. SWE-2 ships exactly three lanes, so | ||
| * advertising low, xhigh or ultra would offer a control that silently rounds to | ||
| * one of these three - the bar registry.ts:418-420 already sets for Anthropic. | ||
| */ | ||
| export const DEVIN_MODEL_EFFORTS: Record<string, string[]> = { | ||
| "swe-2": ["medium", "high", "max"], | ||
| }; | ||
|
|
||
| /** | ||
| * Ladder for a model this table does not name, including anything the live | ||
| * catalog discovers. Five rungs rather than six: `ultra` has no Cognition lane. | ||
| */ | ||
| export const DEVIN_DEFAULT_EFFORTS = ["low", "medium", "high", "xhigh", "max"]; | ||
| ``` | ||
|
|
||
| ## MODIFY: src/providers/registry.ts — devin 행 | ||
|
|
||
| ```ts | ||
| // import 줄 | ||
| import { DEVIN_MODEL_CONTEXT_WINDOWS, DEVIN_MODEL_EFFORTS, DEVIN_DEFAULT_EFFORTS } from "../adapters/devin/live-models"; | ||
|
|
||
| // devin 행 (1340-1358) 끝에 두 줄 | ||
| modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS, | ||
| + modelReasoningEfforts: DEVIN_MODEL_EFFORTS, | ||
| + reasoningEfforts: DEVIN_DEFAULT_EFFORTS, | ||
| ``` | ||
|
|
||
| `modelReasoningEfforts`가 Codex 피커의 모델별 사다리를 정하고(Antigravity 선례), | ||
| `reasoningEfforts`가 Pi 형태 익스포트의 `ExportModel.reasoningEfforts`를 채운다 | ||
| (Anthropic 선례 `df416a439c`). 한 변경이 두 표면을 동시에 고친다. | ||
|
|
||
| ## 왜 SWE-2만 표에 넣는가 | ||
|
|
||
| 라이브 카탈로그의 모델별 실제 suffix 집합은 계정마다 다르고 이 세션에서 실측하지 | ||
| 않았다. 확증된 것은 `SWE2_EFFORT`(`devin.ts:116-126`)가 박아 둔 SWE-2의 3레인뿐이다. | ||
| 나머지는 5단 기본값으로 두고, 실측이 생기면 표에 줄을 추가한다. 모르는 사다리를 | ||
| 지어내는 것보다 낫다. | ||
|
|
||
| ## NEW: tests/providers/devin-effort-ladder.test.ts | ||
|
|
||
| | 케이스 | 기대 | | ||
| |---|---| | ||
| | `DEVIN_MODEL_EFFORTS["swe-2"]` | `["medium","high","max"]` — `low`/`xhigh`/`ultra` 없음 | | ||
| | 표의 모든 사다리가 `SWE2_EFFORT`의 치역에 포함 | 광고와 실제 레인 일치 (드리프트 가드) | | ||
| | devin 레지스트리 행이 두 필드를 모두 노출 | 두 표면 회귀 | | ||
| | `DEVIN_DEFAULT_EFFORTS`에 `ultra` 없음 | Cognition 레인 없음 | | ||
| | omp export가 Devin 모델에 `thinking.mode = "effort"`를 씀 | Pi 회귀 — `management-client-config-route.test.ts:181` Anthropic 케이스 복제 | | ||
|
|
||
| 마지막 줄이 증상 3의 직접 회귀다. 기존 Anthropic 케이스가 그대로 본이 된다. | ||
|
|
||
| ## 레이아웃 등록 | ||
|
|
||
| - `scripts/test-layout/layout.json` `explicit` | ||
| - `tests/fixtures/test-layout-expected.json` | ||
|
|
||
| `devin-adapter.test.ts`가 `providers`로 등록돼 있으니 같은 값을 쓴다. | ||
|
|
||
| ## 범위 밖 | ||
|
|
||
| 레인 C가 #4484 후속으로 남긴 것들 — `src/adapters/registry.ts:26-30`의 구 주석, | ||
| `DEVIN_STATIC_MODELS`에 swe-2 부재, `stale-context-window-migration.ts:45-56`의 구 | ||
| 로스터. 사다리와 무관하므로 이 PR에서 건드리지 않는다. | ||
|
|
||
|
|
||
| ## 계획 수정 — 전 모델 적용 (사용자 지시) | ||
|
|
||
| "swe 뿐만 아니라 모든 devin 모델들에 대해 적용해야" 한다는 지시를 받았다. 정적 표로 | ||
| 전 모델을 채우려면 계정마다 다른 로스터를 지어내야 하므로, 설계를 바꾼다. | ||
|
|
||
| **라이브 카탈로그가 이미 답을 알고 있고 우리가 버리고 있다.** | ||
|
|
||
| `fetchDevinUsableModels`(`live-models.ts:97-127`)는 `catalog.byUid`를 돌면서 | ||
| `collapseDevinModelUid`로 접미사를 벗긴다. 벗겨낸 그 토큰이 곧 그 모델의 실제 | ||
| 사다리다. 지금은 버려지고 base id만 남는다. `contextWindows`를 base별로 모으는 것과 | ||
| 똑같은 자리에서 efforts도 모으면 된다. | ||
|
|
||
| 전달 채널도 이미 있다 — `CatalogModel.reasoningEfforts?: string[]` | ||
| (`parsing.ts:114`)와 `defaultReasoningEffort`(`:115`). | ||
|
|
||
| ### 바뀐 diff 계획 | ||
|
|
||
| **MODIFY `src/adapters/devin/live-models.ts`** — 결과 타입에 `efforts` 추가. | ||
|
|
||
| ```ts | ||
| export type DevinUsableModelsResult = | ||
| | { ok: true; models: string[]; contextWindows: Record<string, number>; | ||
| efforts: Record<string, string[]> } | ||
| | { ok: false; error: "auth" | "http" | "empty" | "unknown"; detail?: string }; | ||
| ``` | ||
|
|
||
| 수집 루프에서, 벗겨낸 토큰 중 Codex 사다리에 해당하는 것만 모은다. | ||
|
|
||
| ```ts | ||
| const efforts = new Map<string, Set<string>>(); | ||
| // ...루프 안, base 계산 직후 | ||
| for (const token of devinEffortTokensOf(entry.modelUid, base)) { | ||
| if (!CODEX_REASONING_RUNGS.has(token)) continue; // fast/priority/1m 제외 | ||
| (efforts.get(base) ?? efforts.set(base, new Set()).get(base)!).add(token); | ||
| } | ||
| ``` | ||
|
|
||
| `fast`, `priority`, `1m`은 추론 단계가 아니라 티어·변형이므로 사다리에서 뺀다. | ||
| 접미사 변형이 하나도 없는 base는 사다리가 비고, 그러면 컨트롤이 안 붙는다 — 그게 | ||
| 정직한 결과다. | ||
|
|
||
| **MODIFY `src/codex/catalog/provider-fetch.ts:1736-1744`** — base별 사다리를 싣는다. | ||
|
|
||
| ```ts | ||
| const result = liveResult.models.map((id) => { | ||
| const liveWindow = liveResult.contextWindows[id]; | ||
| const liveEfforts = liveResult.efforts[id]; | ||
| return { | ||
| id, | ||
| provider: name, | ||
| ...(liveWindow ? { contextWindow: liveWindow } : {}), | ||
| ...(liveEfforts?.length ? { reasoningEfforts: liveEfforts } : {}), | ||
| ...catalogHintsFromProviderConfig(...), | ||
| } as CatalogModel; | ||
| }); | ||
| ``` | ||
|
|
||
| `catalogHintsFromProviderConfig`를 뒤에 두는 순서는 그대로다. contextWindow와 같은 | ||
| 이유로, 사용자가 명시한 오버라이드가 계속 이긴다. | ||
|
|
||
| **MODIFY `src/providers/registry.ts`** — degraded 모드 폴백. | ||
|
|
||
| 라이브 카탈로그가 없을 때(로그인 전, 쿨다운, 네트워크 실패)는 시드 로스터가 쓰인다. | ||
| 그때를 위한 정적 표와, Pi 형태 익스포트가 읽는 프로바이더 레벨 기본값을 둔다. | ||
|
|
||
| ```ts | ||
| modelReasoningEfforts: DEVIN_MODEL_EFFORTS, | ||
| reasoningEfforts: DEVIN_DEFAULT_EFFORTS, | ||
| ``` | ||
|
|
||
| 정적 표에는 실측된 것만 넣는다 — 현재는 `swe-2: ["medium","high","max"]` | ||
| (`SWE2_EFFORT` `devin.ts:116-126`이 박아 둔 3레인). 나머지는 기본값을 쓰고, 로그인 | ||
| 후에는 라이브 값이 덮는다. 모르는 사다리를 지어내지 않는다. | ||
|
|
||
| ### 이 설계가 나은 이유 | ||
|
|
||
| | | 정적 표만 | 라이브 파생 | | ||
| |---|---|---| | ||
| | 커버리지 | 손으로 적은 모델만 | **계정이 가진 전 모델** | | ||
| | 정확도 | 작성 시점 추측 | 계정의 실제 카탈로그 | | ||
| | 새 모델 | 코드 수정 필요 | 자동 | | ||
| | 계정별 차이 | 표현 불가 | 자연히 반영 | | ||
|
|
||
| Antigravity가 정적 표를 쓰는 건 그쪽 로스터가 고정이기 때문이다. Devin은 계정마다 | ||
| 다르고 이미 라이브 디스커버리를 하므로, 같은 목적지에 더 맞는 길이 있다. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1739,6 +1739,12 @@ async function fetchProviderModelsWithAuth( | |
| id, | ||
| provider: name, | ||
| ...(liveWindow ? { contextWindow: liveWindow } : {}), | ||
| // The account catalog names the effort variants each base model has, so | ||
| // its ladder is measured rather than assumed. Without this the entry | ||
| // inherits the generic routed ladder and offers rungs the model rounds | ||
| // away, and every client that keys an effort control off this field — | ||
| // the Pi-shaped exports — renders no control at all. | ||
| ...(liveResult.efforts[id]?.length ? { reasoningEfforts: liveResult.efforts[id] } : {}), | ||
| ...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias), | ||
|
Comment on lines
+1747
to
1748
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For a signed-in Devin provider derived from the registry, Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Preserve measured Devin effort ladders in live catalog rows
The registry comments at 🤖 Prompt for AI Agents |
||
| } as CatalogModel; | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import type { CodexAccountMode, FastWire, OcxProviderConfig } from "../types"; | ||
| import { fastWireDeclarationError } from "./fastwire"; | ||
| import { KIRO_MODELS, KIRO_MODEL_CONTEXT_WINDOWS, KIRO_MODEL_REASONING_EFFORTS } from "./kiro-models"; | ||
| import { DEVIN_MODEL_CONTEXT_WINDOWS } from "../adapters/devin/live-models"; | ||
| import { DEVIN_MODEL_CONTEXT_WINDOWS, DEVIN_MODEL_EFFORTS, DEVIN_DEFAULT_EFFORTS } from "../adapters/devin/live-models"; | ||
| import { ANTIGRAVITY_MODELS, ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, ANTIGRAVITY_MODEL_EFFORTS, ANTIGRAVITY_MODEL_INPUT_MODALITIES } from "./antigravity-models"; | ||
| import type { ProviderBaseUrlChoice } from "./base-url-choices"; | ||
| import { | ||
|
|
@@ -1356,6 +1356,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ | |
| liveModels: true, | ||
| defaultModel: "swe-2", | ||
| modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS, | ||
| // Degraded-mode ladders only. Once a credential is present the account | ||
| // catalog supplies each base model its measured rungs; these two fields are | ||
| // what a signed-out picker and the Pi-shaped client exports fall back to. | ||
| modelReasoningEfforts: DEVIN_MODEL_EFFORTS, | ||
| reasoningEfforts: DEVIN_DEFAULT_EFFORTS, | ||
|
Comment on lines
+1362
to
+1363
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This changes provider catalog and adapter behavior across the owned AGENTS.md reference: src/AGENTS.md:L10-L11 Useful? React with 👍 / 👎. |
||
| }, | ||
| { | ||
| id: "xai", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Run the required validation before merge.
scripts/test-layout/layout.json:648changes configuration loaded byscripts/test-layout/verify.ts. Run a focused test-layout probe for theprovidersdomain,bun run typecheck, andbun run privacy:scan. Thescripts/**guidance requires these checks for this configuration change.bun run prepushis not required because this mapping does not change release, packaging, dependency, or cross-platform tooling. Report any platform-specific validation that was not executed.🤖 Prompt for AI Agents