Skip to content

Wire Settings Models to live model health#380

Open
thebtf wants to merge 1 commit into
mainfrom
work/settings-model-health-live
Open

Wire Settings Models to live model health#380
thebtf wants to merge 1 commit into
mainfrom
work/settings-model-health-live

Conversation

@thebtf

@thebtf thebtf commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • promote the Settings modal Models tab from mustbuild to live read-only using /api/model-health
  • add a typed /api/models registry seam and render its v5-empty state honestly
  • keep credentials, bindings, and model mutation controls as explicit mustbuild evidence

Verification

  • npm ci --prefer-offline
  • npm run test:seam
  • npm run parity
  • npm run build
  • npx playwright test tests/browser/settings-modal.spec.ts
  • npm run test:browser
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Добавлена вкладка "Модели" в настройки консоли оператора для отслеживания состояния и реестра моделей.
    • Реализована панель мониторинга здоровья моделей с отображением статусов (OK, Standby, Degraded) и инструментами обновления.
    • Добавлено отображение реестра моделей с информацией о настроенных моделях и учетных данных.
  • Documentation

    • Добавлена полная локализация интерфейса на английском, русском и китайском языках.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Добавлена вкладка «Models» в SettingsModal: новый composable useModelRegistryState загружает /api/models, расширен интерфейс ModelRow, добавлены ModelRegistrySnapshot и маппинг-хелперы. Шаблон отображает live model-health и registry read-only. Добавлены i18n-строки в трёх локалях, mock-эндпоинт и расширены seam-contract/browser-тесты.

Changes

Вкладка Models в SettingsModal

Layer / File(s) Summary
Контракты данных и composable useModelRegistryState
apps/operator-console/composables/useMockData.ts
ModelRow расширен полями endpoint, configured, secretSet; добавлены ModelRegistrySnapshot, ApiModelsResponse, хелперы mapModelRegistry/modelRegistryName и composable useModelRegistryState, загружающий /api/models.
Подключение вкладки Models в SettingsModal
apps/operator-console/components/SettingsModal.vue
Добавлены импорты useModelsState/useModelRegistryState, расширен SettingsTabKind значением 'models', tab-конфиг переключён на live-evidence GET /api/model-health, введены вычисляемые свойства для health/registry и хелперы modelHealthClass/refreshModelSurfaces.
Шаблон и стили вкладки Models
apps/operator-console/components/SettingsModal.vue
В шаблоне добавлена ветка v-else-if kind==='models' с секциями health, registry и next (включая HonestyBadge mustbuild и кнопку refresh). Добавлены CSS-правила для model-toolbar/model-list/model-row и адаптивные переопределения.
i18n-строки и mock-эндпоинт
apps/operator-console/i18n/locales/en.json, ...ru.json, ...zh.json, apps/operator-console/scripts/mock-operator-api.mjs
Блок settings.models добавлен в три локали (счётчики, health-статусы, registry-состояния, mustbuild-next). В mock-сервер добавлен case '/api/models', возвращающий пустой реестр.
Тесты и PARITY
apps/operator-console/scripts/seam-contract.test.mjs, apps/operator-console/tests/browser/settings-modal.spec.ts, apps/operator-console/PARITY.json
В seam-contract добавлен тест, проверяющий read-only характер вкладки через regex-контракты по исходникам и всем локалям. В browser-spec расширены ожидания для раздела моделей. В PARITY.json уточнено описание gaps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • thebtf/engram#348: Рефактор HonestyBadge/useHonesty напрямую затрагивает компонент HonestyBadge mustbuild, который теперь рендерится в секции next новой вкладки Models.
  • thebtf/engram#366: Оба PR расширяют useMockData.ts через ModelRow/mapModelHealthRow и useModelsState (/api/model-health); данный PR подключает тот же live health-снапшот к новой вкладке SettingsModal.
  • thebtf/engram#360: Playwright-тесты Settings modal, расширенные в данном PR, исполняются в CI-пайплайне, настроенном в этом PR.

Suggested labels

ai-review:passed

🐇 Мои ушки стоят торчком —
модели видны в реестре живом!
Health, registry, next — три раздела,
i18n написан в трёх наречьях смело.
Нет мутаций — всё read-only, честно!
Кролик рад: порядок здесь чудесный. 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок точно отражает основное изменение: интеграция вкладки Models с живым эндпоинтом /api/model-health для вывода из статуса mustbuild в live read-only режим.
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 work/settings-model-health-live

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request transitions the 'Models' tab in the settings modal from a mockup placeholder to a live, read-only view displaying model health and registry information. It integrates the /api/model-health and /api/models endpoints, introduces state management composables, adds comprehensive localization, and updates tests to verify the new functionality. The review feedback suggests enhancing the user experience by automatically refreshing the model data when the tab is opened to prevent stale views, and adding defensive checks to prevent potential runtime errors if the API returns malformed non-array data.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +240 to +245
async function refreshModelSurfaces() {
await Promise.all([
modelHealthState.refresh(),
modelRegistryState.refresh(),
])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The model health and registry states are currently only loaded once when the application starts (via startOnce). When an operator opens the settings modal or switches to the 'Models' tab, they might be presented with stale data if the status has changed in the background. It is highly recommended to automatically refresh these surfaces when the modal is opened and the 'models' tab is active.

async function refreshModelSurfaces() {
  await Promise.all([
    modelHealthState.refresh(),
    modelRegistryState.refresh(),
  ])
}

watch([open, activeTab], ([isOpen, tab]) => {
  if (isOpen && tab === 'models') {
    refreshModelSurfaces()
  }
}, { immediate: true })

Comment on lines +456 to +462
function mapModelRegistry(payload: ApiModelsResponse | undefined): ModelRegistrySnapshot {
return {
models: [...new Set((payload?.models || []).map(modelRegistryName).filter((value): value is string => Boolean(value)))],
defaultModel: modelRegistryName(payload?.default) || '—',
currentModel: modelRegistryName(payload?.current) || '—',
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the API returns a malformed response where models is not an array (e.g., null or an object), (payload?.models || []) will evaluate to that non-array value, causing .map() to throw a TypeError and crash the state refresh. It is safer to explicitly check if payload?.models is an array using Array.isArray.

Suggested change
function mapModelRegistry(payload: ApiModelsResponse | undefined): ModelRegistrySnapshot {
return {
models: [...new Set((payload?.models || []).map(modelRegistryName).filter((value): value is string => Boolean(value)))],
defaultModel: modelRegistryName(payload?.default) || '—',
currentModel: modelRegistryName(payload?.current) || '—',
}
}
function mapModelRegistry(payload: ApiModelsResponse | undefined): ModelRegistrySnapshot {
const rawModels = payload && Array.isArray(payload.models) ? payload.models : []
return {
models: [...new Set(rawModels.map(modelRegistryName).filter((value): value is string => Boolean(value)))],
defaultModel: modelRegistryName(payload?.default) || '—',
currentModel: modelRegistryName(payload?.current) || '—',
}
}

@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: 1

🤖 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 `@apps/operator-console/composables/useMockData.ts`:
- Around line 456-459: The mapModelRegistry function assumes that
payload?.models is either falsy or an array, but if the server returns a truthy
non-array value, calling .map() will throw a TypeError. Normalize
payload?.models by adding an Array.isArray() check to ensure it's always treated
as an array before calling .map(modelRegistryName). Replace the current fallback
logic (payload?.models || []) with a proper type guard that returns an empty
array if payload?.models exists but is not an array.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58cf6e16-5057-4a39-9a5d-8cb97f20bbfb

📥 Commits

Reviewing files that changed from the base of the PR and between dacb9ac and 4cecdb3.

📒 Files selected for processing (9)
  • apps/operator-console/PARITY.json
  • apps/operator-console/components/SettingsModal.vue
  • apps/operator-console/composables/useMockData.ts
  • apps/operator-console/i18n/locales/en.json
  • apps/operator-console/i18n/locales/ru.json
  • apps/operator-console/i18n/locales/zh.json
  • apps/operator-console/scripts/mock-operator-api.mjs
  • apps/operator-console/scripts/seam-contract.test.mjs
  • apps/operator-console/tests/browser/settings-modal.spec.ts

Comment on lines +456 to +459
function mapModelRegistry(payload: ApiModelsResponse | undefined): ModelRegistrySnapshot {
return {
models: [...new Set((payload?.models || []).map(modelRegistryName).filter((value): value is string => Boolean(value)))],
defaultModel: modelRegistryName(payload?.default) || '—',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Нормализуйте payload.models перед вызовом .map()

На Line 458 используется (payload?.models || []).map(...). Если сервер вернёт truthy-значение, которое не является массивом, здесь будет TypeError, и вкладка реестра уйдёт в ошибку из-за формы payload, а не из-за бизнес-ошибки.

💡 Предлагаемое исправление
 function mapModelRegistry(payload: ApiModelsResponse | undefined): ModelRegistrySnapshot {
+  const models = Array.isArray(payload?.models) ? payload.models : []
   return {
-    models: [...new Set((payload?.models || []).map(modelRegistryName).filter((value): value is string => Boolean(value)))],
+    models: [...new Set(models.map(modelRegistryName).filter((value): value is string => Boolean(value)))],
     defaultModel: modelRegistryName(payload?.default) || '—',
     currentModel: modelRegistryName(payload?.current) || '—',
   }
 }
🤖 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 `@apps/operator-console/composables/useMockData.ts` around lines 456 - 459, The
mapModelRegistry function assumes that payload?.models is either falsy or an
array, but if the server returns a truthy non-array value, calling .map() will
throw a TypeError. Normalize payload?.models by adding an Array.isArray() check
to ensure it's always treated as an array before calling
.map(modelRegistryName). Replace the current fallback logic (payload?.models ||
[]) with a proper type guard that returns an empty array if payload?.models
exists but is not an array.

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