agy 모델 카탈로그 탭 형식 파서 수정과 표준 경로 폴백

agy CLI가 2026-08부터 'agy models'를 'model_id<TAB>라벨' 형태로 출력하는데
_discover_agy_cli가 공백 포함 줄을 전부 버려 카탈로그가 비어 available=false가 됐다
(관리자 AI 운영 화면에서 'Agy가 선택 가능한 모델을 반환하지 않았습니다').
- 탭 줄은 왼쪽 토큰을 id로, CLI 라벨을 우선 적용. 탭 없는 공백 줄(상태 문구)은 계속 무시하고
  구형 베어 id 출력 계약 유지 — 형식 이중 호환
- _binary가 PATH에 없어도 LOCALAPPDATA agy bin 표준 설치 경로를 폴백으로 탐색
- 검증: 신규 탭 형식 회귀 RED→GREEN, engine_gateway 63 passed, 실제 agy CLI로
  15모델 live 조회 확인(default gemini-3.6-flash-high)
This commit is contained in:
Yun Chan 2026-08-18 13:09:42 +09:00
parent 0d5e211bd5
commit 5afd92f8f3
2 changed files with 54 additions and 2 deletions

View file

@ -117,6 +117,39 @@ class ProviderRegistryTest(unittest.IsolatedAsyncioTestCase):
self.assertEqual(selected.reasoning_efforts, ["high"])
self.assertEqual(selected.label, "Gemini 3.6 Flash (High)")
async def test_agy_catalog_parses_tab_separated_id_label_lines(self):
# 2026-08 agy CLI는 `agy models`를 "model_id\t표시 라벨" 형태로 출력한다.
# 탭이 포함된 줄을 통째로 버리면 카탈로그가 비어 available=false가 된다.
stdout = "\n".join(
[
"gemini-3.7-flash-high\tGemini 3.7 Flash (High)",
"gemini-3.7-flash-medium\tGemini 3.7 Flash (Medium)",
"claude-sonnet-4-6\tClaude Sonnet 4.6 (Thinking)",
"Fetching available models...",
]
)
with (
patch.object(provider_registry, "_binary", return_value="agy.exe"),
patch.object(
provider_registry,
"_run_process",
AsyncMock(return_value=(stdout, "")),
),
):
result = await provider_registry.discover_capabilities("agy_cli")
self.assertTrue(result.available)
self.assertEqual(
[model.id for model in result.models],
["gemini-3.7-flash-high", "gemini-3.7-flash-medium", "claude-sonnet-4-6"],
)
# 기본 모델 gemini-3.6-flash-high 가 목록에 없으면 첫 모델로 폴백한다.
self.assertEqual(result.default_model, "gemini-3.7-flash-high")
self.assertEqual(result.default_reasoning_effort, "high")
selected = next(model for model in result.models if model.id == result.default_model)
self.assertEqual(selected.reasoning_efforts, ["high"])
self.assertEqual(selected.label, "Gemini 3.7 Flash (High)")
async def test_claude_cli_catalog_is_explicit_static_alias_fallback(self):
with patch.object(provider_registry, "_binary", return_value="claude.exe"):
result = await provider_registry.discover_capabilities("claude_cli")