From 3ca960f3881ca41eb3a542c66cd4154f1310695e Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 22 Oct 2026 14:29:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=A1=B5=E9=9D=A2=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=B9=B6=E5=A2=9E=E5=BC=BA=20Provider=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 表单下拉框不再用 label 包裹,改为 div + aria-label,避免点击后 下拉面板瞬间关闭 - Provider 卡片新增「从上游获取模型」和「修改」入口,支持编辑 base URL、API Key 与 api_mode,凭据写入对应的环境变量 - 服务端抽出 provider-model-discovery 统一不同 api_mode 的模型发现, 旧的 custom_providers 与新的 providers 字典结构都能正确更新 - Agent Bridge 改为后台启动,HTTP 接口在 Python 运行时就绪前即可用; worker 连接被拒时按类型重试,不重放可能已产生副作用的请求 - 断线重连后恢复 profiles、models 与会话列表,健康检查改为自调度 的 setTimeout,离线时 2 秒后重试 - 批量删除确认文案改用 Popconfirm content,避免变成侧栏触发器文本 --- .../2026-10-22-http-startup-recovery.md | 26 ++++++ .../2026-10-22-worker-connect-readiness.md | 21 +++++ docs/openapi.json | 9 ++ packages/client/src/api/hermes/system.ts | 4 + .../src/components/hermes/chat/ChatPanel.vue | 21 +++-- .../hermes/models/AuxiliaryModelsPanel.vue | 13 ++- .../components/hermes/models/ProviderCard.vue | 32 +++++++ .../hermes/models/ProviderEditModal.vue | 78 ++++++++++++++++ .../hermes/models/ProviderFormModal.vue | 1 + packages/client/src/i18n/locales/en.ts | 8 ++ packages/client/src/i18n/locales/zh.ts | 8 ++ packages/client/src/stores/hermes/app.ts | 15 ++- packages/client/src/stores/hermes/chat.ts | 4 +- packages/client/src/views/hermes/ChatView.vue | 18 +++- .../src/views/hermes/GlobalAgentView.vue | 18 +++- .../client/src/views/hermes/HistoryView.vue | 8 ++ .../client/src/views/hermes/WorkflowView.vue | 5 +- .../server/src/controllers/hermes/models.ts | 41 +++----- .../src/controllers/hermes/providers.ts | 43 ++++++--- packages/server/src/index.ts | 11 ++- .../services/hermes/agent-bridge/client.ts | 21 ++++- .../hermes/custom-providers-compat.ts | 4 +- .../hermes/provider-model-discovery.ts | 30 ++++++ tests/client/app-store.test.ts | 29 ++++++ tests/client/chat-store-session-order.test.ts | 14 +++ .../client/dropdown-label-activation.test.ts | 43 +++++++++ tests/e2e/chat-input-dropdown.spec.ts | 37 ++++++++ tests/e2e/chat-startup-recovery.spec.ts | 47 ++++++++++ tests/e2e/form-dropdowns.spec.ts | 45 +++++++++ tests/e2e/provider-card-actions.spec.ts | 93 +++++++++++++++++++ tests/e2e/provider-models.spec.ts | 2 +- .../agent-bridge-worker-readiness.test.ts | 83 +++++++++++++++++ .../model-visibility-controller.test.ts | 20 ++++ tests/server/provider-model-discovery.test.ts | 39 ++++++++ .../server/provider-update-controller.test.ts | 63 +++++++++++++ 35 files changed, 876 insertions(+), 78 deletions(-) create mode 100644 docs/chat-chain-changes/2026-10-22-http-startup-recovery.md create mode 100644 docs/chat-chain-changes/2026-10-22-worker-connect-readiness.md create mode 100644 packages/client/src/components/hermes/models/ProviderEditModal.vue create mode 100644 packages/server/src/services/hermes/provider-model-discovery.ts create mode 100644 tests/client/dropdown-label-activation.test.ts create mode 100644 tests/e2e/chat-startup-recovery.spec.ts create mode 100644 tests/e2e/form-dropdowns.spec.ts create mode 100644 tests/e2e/provider-card-actions.spec.ts create mode 100644 tests/server/agent-bridge-worker-readiness.test.ts create mode 100644 tests/server/provider-model-discovery.test.ts diff --git a/docs/chat-chain-changes/2026-10-22-http-startup-recovery.md b/docs/chat-chain-changes/2026-10-22-http-startup-recovery.md new file mode 100644 index 0000000..aa36d96 --- /dev/null +++ b/docs/chat-chain-changes/2026-10-22-http-startup-recovery.md @@ -0,0 +1,26 @@ +--- +date: 2026-10-22 +feature: HTTP startup and session reload after backend restart +impact: Keep session APIs available during bridge startup and recover failed initial loads +--- + +PR/commit: pending. + +The HTTP listener, Web UI stores, routes and Socket.IO handlers are initialized +before starting the managed Agent Bridge in the background. Bridge readiness +continues to be checked by the existing chat handlers; the chat-run protocol is +unchanged. The bridge manager is registered with shutdown before starting it. + +A failed session-list request no longer marks the list loaded or discards a +route-selected session. Offline health polling retries after two seconds; +reconnection reloads profiles, models and sessions. An already loaded session +list retains its state on request failure. Chat, global-agent and history views +handle reconnection. Batch-delete confirmation text uses Popconfirm content +rather than becoming part of the sidebar trigger. + +Validation: app-store and session-order Vitest coverage; Playwright startup +recovery (base route and direct session route), deletion confirmation and history +deep links; build and harness checks. A real isolated built-server smoke check +used a fake Python executable sleeping for 60 seconds and confirmed /health +responded with status=ok before the bridge became ready. State, uploads and +bridge endpoint were isolated under a temporary directory. diff --git a/docs/chat-chain-changes/2026-10-22-worker-connect-readiness.md b/docs/chat-chain-changes/2026-10-22-worker-connect-readiness.md new file mode 100644 index 0000000..99314aa --- /dev/null +++ b/docs/chat-chain-changes/2026-10-22-worker-connect-readiness.md @@ -0,0 +1,21 @@ +--- +date: 2026-10-22 +feature: Agent Bridge worker connection readiness +impact: Recover refused worker connections before returning a chat error +--- + +# Worker connection readiness recovery + +- Date: 2026-10-22 +- PR/commit: pending +- Feature: Agent Bridge client, cold and idle worker connection recovery. +- Behavior: Retry broker responses explicitly typed as worker connection refusal + or missing socket, within the existing connect retry budget. Keep the original + payload (including session and conversation history). The broker's own ping + does not establish worker readiness. Previously only Node-to-broker connection + failures were retried; Python-to-worker connect failures escaped to chat. +- Safety: Do not replay untyped agent errors, timeouts, closed responses, or + resets, because the worker might already have accepted the request. Vendored + Python and the chat-run event contract remain unchanged. +- Validation: focused worker readiness and existing chat/bridge tests; build and + harness checks. diff --git a/docs/openapi.json b/docs/openapi.json index ca653d5..0f458c5 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -3170,6 +3170,12 @@ }, "name": { "type": "string" + }, + "providerKey": { + "type": "string" + }, + "source": { + "type": "string" } } } @@ -7688,6 +7694,9 @@ "api_key": { "type": "string" }, + "api_mode": { + "type": "object" + }, "base_url": { "type": "string" }, diff --git a/packages/client/src/api/hermes/system.ts b/packages/client/src/api/hermes/system.ts index 4873928..0ae5746 100644 --- a/packages/client/src/api/hermes/system.ts +++ b/packages/client/src/api/hermes/system.ts @@ -100,6 +100,7 @@ export interface AvailableModelGroup { builtin?: boolean /** Env var used by Hermes to override this provider's base URL. If present, the preset URL is editable. */ base_url_env?: string + credentials_editable?: boolean /** Config source for custom providers. Dict-backed providers can be deleted from providers:. */ provider_source?: 'custom_providers' | 'providers' provider_key?: string @@ -224,6 +225,7 @@ export async function fetchProviderModels(data: { provider?: string label?: string update_cache?: boolean + api_mode?: ProviderApiMode }): Promise<{ models: string[] }> { return request<{ models: string[] }>('/api/hermes/provider-models', { method: 'POST', @@ -277,6 +279,8 @@ export async function removeCustomProvider(name: string, options: { source?: 'cu } export async function updateProvider(poolKey: string, data: { + source?: 'custom_providers' | 'providers' + providerKey?: string name?: string base_url?: string api_key?: string diff --git a/packages/client/src/components/hermes/chat/ChatPanel.vue b/packages/client/src/components/hermes/chat/ChatPanel.vue index cd46149..6b61625 100644 --- a/packages/client/src/components/hermes/chat/ChatPanel.vue +++ b/packages/client/src/components/hermes/chat/ChatPanel.vue @@ -1125,13 +1125,16 @@ async function handleSessionModelCustomSubmit() { - + - {{ t('chat.confirmBatchDelete', { count: selectedCount }) }}
-