@goodandready/dsh-model-sync 0.5.10 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/README.ru.md +17 -9
- package/README.zh.md +17 -9
- package/lib/adapter-registry.js +6 -0
- package/lib/alias-resolver.js +16 -2
- package/lib/client.js +416 -32
- package/lib/diff-notifier.js +12 -1
- package/lib/generic-adapter.js +9 -1
- package/lib/inventory.js +16 -8
- package/lib/models.js +28 -3
- package/lib/reconcile.js +2 -0
- package/lib/reliability.js +86 -14
- package/lib/synchronizer-prober.js +130 -50
- package/lib/synchronizer.js +43 -1
- package/lib/updater.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,23 +137,31 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
137
137
|
```yaml
|
|
138
138
|
dsh-model-sync:
|
|
139
139
|
enabled: true
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
scheduleEnabled: false
|
|
141
|
+
intervalMinutes: 60
|
|
142
|
+
autoApply: false
|
|
143
|
+
requestTimeoutMs: 15000
|
|
144
|
+
retryAttempts: 3
|
|
145
|
+
concurrency: 4
|
|
143
146
|
providers:
|
|
144
|
-
commandcode
|
|
147
|
+
- provider: commandcode
|
|
145
148
|
enabled: true
|
|
146
|
-
|
|
149
|
+
autoApply: false
|
|
150
|
+
- provider: clinebot
|
|
147
151
|
enabled: true
|
|
152
|
+
autoApply: false
|
|
148
153
|
```
|
|
149
154
|
|
|
150
155
|
| Parameter | Type | Default | Description |
|
|
151
156
|
|---|---|---|---|
|
|
152
157
|
| `enabled` | `boolean` | `true` | Master switch for background synchronization |
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
158
|
+
| `scheduleEnabled` | `boolean` | `false` | Enable periodic background synchronization schedule |
|
|
159
|
+
| `intervalMinutes` | `number` | `60` | Polling frequency for catalog refreshes in minutes |
|
|
160
|
+
| `autoApply` | `boolean` | `false` | Automatically apply discovered models to active catalog |
|
|
161
|
+
| `requestTimeoutMs`| `number` | `15000` | HTTP request timeout for catalog fetching (ms) |
|
|
162
|
+
| `retryAttempts` | `number` | `3` | Maximum retry attempts for transient network failures |
|
|
163
|
+
| `concurrency` | `number` | `4` | Maximum parallel provider synchronization workers |
|
|
164
|
+
| `providers` | `array` | `[]` | Per-provider configuration list (`provider`, `enabled`, `autoApply`) |
|
|
157
165
|
|
|
158
166
|
---
|
|
159
167
|
|
package/README.ru.md
CHANGED
|
@@ -137,23 +137,31 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
137
137
|
```yaml
|
|
138
138
|
dsh-model-sync:
|
|
139
139
|
enabled: true
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
scheduleEnabled: false
|
|
141
|
+
intervalMinutes: 60
|
|
142
|
+
autoApply: false
|
|
143
|
+
requestTimeoutMs: 15000
|
|
144
|
+
retryAttempts: 3
|
|
145
|
+
concurrency: 4
|
|
143
146
|
providers:
|
|
144
|
-
commandcode
|
|
147
|
+
- provider: commandcode
|
|
145
148
|
enabled: true
|
|
146
|
-
|
|
149
|
+
autoApply: false
|
|
150
|
+
- provider: clinebot
|
|
147
151
|
enabled: true
|
|
152
|
+
autoApply: false
|
|
148
153
|
```
|
|
149
154
|
|
|
150
155
|
| Параметр | Тип | По умолчанию | Описание |
|
|
151
156
|
|---|---|---|---|
|
|
152
157
|
| `enabled` | `boolean` | `true` | Главный переключатель фоновой синхронизации |
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
158
|
+
| `scheduleEnabled` | `boolean` | `false` | Включение периодического фонового расписания синхронизации |
|
|
159
|
+
| `intervalMinutes` | `number` | `60` | Периодичность опроса провайдеров (в минутах) |
|
|
160
|
+
| `autoApply` | `boolean` | `false` | Автоматически применять обнаруженные модели к активному каталогу |
|
|
161
|
+
| `requestTimeoutMs`| `number` | `15000` | Таймаут HTTP-запросов синхронизации каталогов (в миллисекундах) |
|
|
162
|
+
| `retryAttempts` | `number` | `3` | Максимальное число повторных попыток при сетевых сбоях |
|
|
163
|
+
| `concurrency` | `number` | `4` | Максимальное количество параллельных задач синхронизации |
|
|
164
|
+
| `providers` | `array` | `[]` | Список индивидуальных настроек провайдеров (`provider`, `enabled`, `autoApply`) |
|
|
157
165
|
|
|
158
166
|
---
|
|
159
167
|
|
package/README.zh.md
CHANGED
|
@@ -137,23 +137,31 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
137
137
|
```yaml
|
|
138
138
|
dsh-model-sync:
|
|
139
139
|
enabled: true
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
scheduleEnabled: false
|
|
141
|
+
intervalMinutes: 60
|
|
142
|
+
autoApply: false
|
|
143
|
+
requestTimeoutMs: 15000
|
|
144
|
+
retryAttempts: 3
|
|
145
|
+
concurrency: 4
|
|
143
146
|
providers:
|
|
144
|
-
commandcode
|
|
147
|
+
- provider: commandcode
|
|
145
148
|
enabled: true
|
|
146
|
-
|
|
149
|
+
autoApply: false
|
|
150
|
+
- provider: clinebot
|
|
147
151
|
enabled: true
|
|
152
|
+
autoApply: false
|
|
148
153
|
```
|
|
149
154
|
|
|
150
155
|
| 参数名称 | 类型 | 默认值 | 功能说明 |
|
|
151
156
|
|---|---|---|---|
|
|
152
157
|
| `enabled` | `boolean` | `true` | 后台自动同步主开关 |
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
158
|
+
| `scheduleEnabled` | `boolean` | `false` | 启用周期性后台定时同步任务 |
|
|
159
|
+
| `intervalMinutes` | `number` | `60` | 目录更新轮询间隔(分钟) |
|
|
160
|
+
| `autoApply` | `boolean` | `false` | 是否自动将新发现的模型生效至可用列表 |
|
|
161
|
+
| `requestTimeoutMs`| `number` | `15000` | 目录拉取 HTTP 请求超时时间(毫秒) |
|
|
162
|
+
| `retryAttempts` | `number` | `3` | 瞬态网络故障的最大重试次数 |
|
|
163
|
+
| `concurrency` | `number` | `4` | 最大并发服务商同步任务数 |
|
|
164
|
+
| `providers` | `array` | `[]` | 服务商独立配置列表(`provider`、`enabled`、`autoApply`) |
|
|
157
165
|
|
|
158
166
|
---
|
|
159
167
|
|
package/lib/adapter-registry.js
CHANGED
|
@@ -219,6 +219,9 @@ async function requestSpecific(profile, descriptor, { resolveCredential, fetchIm
|
|
|
219
219
|
}
|
|
220
220
|
if (response.status === 304) return response
|
|
221
221
|
if (!response.ok) {
|
|
222
|
+
if (typeof response.body?.cancel === "function") {
|
|
223
|
+
try { await response.body.cancel() } catch (_err) { /* best-effort cancel */ }
|
|
224
|
+
}
|
|
222
225
|
const error = catalogRequestError(response.status, response)
|
|
223
226
|
error.adapterCode = [401, 403].includes(response.status) ? 'auth' : response.status === 429 ? 'rate-limit' : 'http'
|
|
224
227
|
throw error
|
|
@@ -231,6 +234,9 @@ async function fetchSpecific(profile, descriptor, options) {
|
|
|
231
234
|
try {
|
|
232
235
|
const response = await requestSpecific(profile, descriptor, options)
|
|
233
236
|
if (response.status === 304) {
|
|
237
|
+
if (typeof response.body?.cancel === "function") {
|
|
238
|
+
try { await response.body.cancel() } catch (_err) { /* best-effort cancel */ }
|
|
239
|
+
}
|
|
234
240
|
return { notModified: true, etag: response.headers?.get?.('etag') || options.etag, lastModified: response.headers?.get?.('last-modified') || options.lastModified }
|
|
235
241
|
}
|
|
236
242
|
const respEtag = response.headers?.get?.('etag') || undefined
|
package/lib/alias-resolver.js
CHANGED
|
@@ -53,6 +53,16 @@ function estimateLatency(provider, model, probes = {}) {
|
|
|
53
53
|
return Math.round(base)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
function isProviderEligible(provider, { healthResults = [], isCircuitOpen = () => false } = {}) {
|
|
57
|
+
if (isCircuitOpen(provider)) return false
|
|
58
|
+
const health = healthResults.find((h) => h.provider === provider)
|
|
59
|
+
if (health) {
|
|
60
|
+
if (health.ok === false) return false
|
|
61
|
+
if (health.status && health.status !== "ok") return false
|
|
62
|
+
}
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
export function resolveAlias(aliasName, {
|
|
57
67
|
aliases = {},
|
|
58
68
|
modelCatalogs = {},
|
|
@@ -72,7 +82,8 @@ export function resolveAlias(aliasName, {
|
|
|
72
82
|
for (let i = 0; i < custom.targets.length; i++) {
|
|
73
83
|
const target = custom.targets[i]
|
|
74
84
|
const circuitBlocked = isCircuitOpen(target.provider)
|
|
75
|
-
const
|
|
85
|
+
const healthEntry = healthResults.find((h) => h.provider === target.provider)
|
|
86
|
+
const healthFail = Boolean(healthEntry && (healthEntry.status === "error" || healthEntry.status === "circuit-open" || healthEntry.ok === false || (healthEntry.status && healthEntry.status !== "ok")))
|
|
76
87
|
if (circuitBlocked || healthFail) {
|
|
77
88
|
skipped.push({ ...target, reason: circuitBlocked ? 'circuit_open' : 'health_failed' })
|
|
78
89
|
continue
|
|
@@ -101,12 +112,15 @@ export function resolveAlias(aliasName, {
|
|
|
101
112
|
// 2. Static explicit alias
|
|
102
113
|
if (custom?.provider && custom?.model && !custom.strategy) {
|
|
103
114
|
const circuitBlocked = isCircuitOpen(custom.provider)
|
|
115
|
+
const healthEntry = healthResults.find((h) => h.provider === custom.provider)
|
|
116
|
+
const healthFailed = Boolean(healthEntry && (healthEntry.status === "error" || healthEntry.status === "circuit-open" || healthEntry.ok === false || (healthEntry.status && healthEntry.status !== "ok")))
|
|
104
117
|
return {
|
|
105
118
|
alias: clean,
|
|
106
119
|
provider: custom.provider,
|
|
107
120
|
model: custom.model,
|
|
108
121
|
fallbackUsed: false,
|
|
109
122
|
...(circuitBlocked ? { warning: 'circuit_open', circuitOpen: true } : {}),
|
|
123
|
+
...(healthFailed ? { warning: 'health_failed', healthFailed: true } : {}),
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
126
|
|
|
@@ -125,7 +139,7 @@ export function resolveAlias(aliasName, {
|
|
|
125
139
|
}
|
|
126
140
|
const available = []
|
|
127
141
|
for (const [provider, models] of Object.entries(modelCatalogs)) {
|
|
128
|
-
if (
|
|
142
|
+
if (!isProviderEligible(provider, { healthResults, isCircuitOpen })) continue
|
|
129
143
|
if (!Array.isArray(models)) continue
|
|
130
144
|
for (const m of models) {
|
|
131
145
|
if (!m?.id) continue
|