mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-28 03:01:54 +08:00
feat(hermes): dynamically load provider registry in setup & dashboard (Step 2)
Wire the new Rust `hermes_list_providers` command into the frontend and
replace the hardcoded OpenClaw PROVIDER_PRESETS usage with the
authoritative Hermes registry. Closes the G4 gap from the v3 design.
New module `src/engines/hermes/lib/providers.js`:
- Async `loadHermesProviders()` with per-session cache.
- `groupProviders()` buckets by authType + region: apiKeyIntl,
apiKeyCn, aggregators, oauth, externalProc, custom.
- Lookup helpers: `findProviderById`, `inferProviderByBaseUrl`,
`defaultModelFor`, plus cache reset for hot-reload scenarios.
- Exported auth_type / transport string constants mirroring Rust.
Refactored `src/engines/hermes/pages/setup.js`:
- Drops `PROVIDER_PRESETS` import; loads registry before first paint.
- Provider buttons are grouped under labeled sections (International,
China, Aggregators), with an OAuth hint block listing the CLI
commands users must run (e.g. `hermes auth login nous`).
- Selected provider detail panel now shows target env var
(`ANTHROPIC_API_KEY`, `DEEPSEEK_API_KEY`, etc.) and model catalog
size.
- `doSaveConfig` sends the provider id (not preset key) through
`api.configureHermes`; falls back to `custom` when the base URL
doesn't match any registered provider.
- `doFetchModels` maps provider.transport → apiType.
- Graceful fallback: when the registry is empty (Web mode), UI
degrades to manual Base URL + API Key entry.
Refactored `src/engines/hermes/pages/dashboard.js`:
- Loads provider registry in parallel with gateway refresh.
- Preset buttons filter out the `custom` placeholder and source
data from the async-loaded list.
- Uses `inferProviderByBaseUrl` consistently for highlight / fetch /
save flows.
Frontend API wiring:
- `src/lib/tauri-api.js`:
- Added `hermesListProviders` (10-minute cache).
- Extended `hermesFetchModels` and `hermesUpdateModel` with
optional `provider` param.
- `scripts/dev-api.js`:
- `hermes_list_providers`: Web-mode stub returning [] (triggers
frontend fallback UI).
- `hermes_fetch_models` / `hermes_update_model` accept provider
param (no-op in fetch; full YAML rewrite in update_model matching
Rust behavior).
Verified: `npm run build` green (1.04s). Setup chunk 24.34 kB,
dashboard chunk 24.30 kB. No new warnings.
This commit is contained in:
@@ -6836,7 +6836,16 @@ const handlers = {
|
||||
return { model: displayModel, model_raw: modelName, base_url: baseUrl, provider, api_key: apiKey, config_exists: fs.existsSync(configPath) }
|
||||
},
|
||||
|
||||
async hermes_fetch_models({ baseUrl, apiKey, apiType } = {}) {
|
||||
// Web-mode stub: the authoritative 22-provider registry lives in Rust.
|
||||
// Web mode is primarily used for remote admin on headless Linux where
|
||||
// Hermes configuration is a minor flow; returning an empty array makes
|
||||
// the frontend fall back to a "Please use the desktop app to configure
|
||||
// Hermes providers" message in setup.js.
|
||||
hermes_list_providers() {
|
||||
return []
|
||||
},
|
||||
|
||||
async hermes_fetch_models({ baseUrl, apiKey, apiType, provider: _provider } = {}) {
|
||||
const api = apiType || 'openai'
|
||||
let base = baseUrl.replace(/\/+$/, '')
|
||||
for (const suffix of ['/chat/completions', '/completions', '/responses', '/messages', '/models']) {
|
||||
@@ -6867,20 +6876,68 @@ const handlers = {
|
||||
return models.sort()
|
||||
},
|
||||
|
||||
hermes_update_model({ model } = {}) {
|
||||
hermes_update_model({ model, provider } = {}) {
|
||||
const configPath = path.join(hermesHome(), 'config.yaml')
|
||||
const content = fs.readFileSync(configPath, 'utf8')
|
||||
let found = false
|
||||
const newContent = content.split('\n').map(line => {
|
||||
const lines = content.split('\n')
|
||||
const out = []
|
||||
let inModel = false
|
||||
let defaultWritten = false
|
||||
let providerWritten = false
|
||||
let defaultIndent = ' '
|
||||
|
||||
for (const line of lines) {
|
||||
const t = line.trim()
|
||||
if (t.startsWith('default:') && !found) {
|
||||
found = true
|
||||
const indent = line.length - line.trimStart().length
|
||||
return ' '.repeat(indent) + `default: ${model}`
|
||||
if (t.startsWith('model:')) {
|
||||
inModel = true
|
||||
out.push(line)
|
||||
continue
|
||||
}
|
||||
return line
|
||||
}).join('\n')
|
||||
if (!found) throw new Error('config.yaml 中未找到 model.default 字段')
|
||||
if (inModel) {
|
||||
const isIndented = line.startsWith(' ') || line.startsWith('\t')
|
||||
if (!isIndented && t && !t.startsWith('#')) {
|
||||
// leaving model block — flush provider if needed
|
||||
if (provider && provider !== 'custom' && !providerWritten) {
|
||||
out.push(`${defaultIndent}provider: ${provider}`)
|
||||
providerWritten = true
|
||||
}
|
||||
inModel = false
|
||||
out.push(line)
|
||||
continue
|
||||
}
|
||||
if (t.startsWith('default:')) {
|
||||
const indentLen = line.length - line.trimStart().length
|
||||
defaultIndent = ' '.repeat(indentLen)
|
||||
out.push(`${defaultIndent}default: ${model}`)
|
||||
defaultWritten = true
|
||||
continue
|
||||
}
|
||||
if (t.startsWith('provider:')) {
|
||||
if (provider && provider !== 'custom') {
|
||||
const indentLen = line.length - line.trimStart().length
|
||||
out.push(`${' '.repeat(indentLen)}provider: ${provider}`)
|
||||
providerWritten = true
|
||||
continue
|
||||
}
|
||||
if (provider === 'custom') continue // drop
|
||||
// no new provider → keep old
|
||||
out.push(line)
|
||||
providerWritten = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
out.push(line)
|
||||
}
|
||||
|
||||
// still in model block at EOF
|
||||
if (inModel && provider && provider !== 'custom' && !providerWritten) {
|
||||
out.push(`${defaultIndent}provider: ${provider}`)
|
||||
}
|
||||
|
||||
if (!defaultWritten) throw new Error('config.yaml 中未找到 model.default 字段')
|
||||
|
||||
let newContent = out.join('\n')
|
||||
if (!newContent.endsWith('\n')) newContent += '\n'
|
||||
fs.writeFileSync(configPath, newContent)
|
||||
return `模型已切换为 ${model}`
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user