@swarmclawai/swarmclaw 1.2.5 → 1.2.6
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/package.json
CHANGED
|
@@ -327,8 +327,16 @@ export async function POST(req: Request) {
|
|
|
327
327
|
return NextResponse.json(result)
|
|
328
328
|
}
|
|
329
329
|
default: {
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
let configs: Record<string, { name?: string; baseUrl?: string; isEnabled?: boolean }>
|
|
331
|
+
try {
|
|
332
|
+
const storage = await import('@/lib/server/storage')
|
|
333
|
+
configs = storage.loadProviderConfigs() as Record<string, { name?: string; baseUrl?: string; isEnabled?: boolean }>
|
|
334
|
+
} catch {
|
|
335
|
+
return NextResponse.json(
|
|
336
|
+
{ ok: false, message: `Failed to load provider configurations while checking ${provider}.` },
|
|
337
|
+
{ status: 500 },
|
|
338
|
+
)
|
|
339
|
+
}
|
|
332
340
|
const custom = configs[provider]
|
|
333
341
|
if (custom?.baseUrl) {
|
|
334
342
|
const result = await checkOpenAiCompatible(
|
|
@@ -76,3 +76,111 @@ test('builtin provider override records do not surface as custom providers', ()
|
|
|
76
76
|
|
|
77
77
|
assert.equal(output.openAiCount, 1)
|
|
78
78
|
})
|
|
79
|
+
|
|
80
|
+
test('custom provider resolution includes defaultEndpoint and optionalApiKey', () => {
|
|
81
|
+
const output = runWithTempDataDir<{
|
|
82
|
+
defaultEndpoint: string | null
|
|
83
|
+
optionalApiKey: boolean | null
|
|
84
|
+
requiresApiKey: boolean | null
|
|
85
|
+
}>(`
|
|
86
|
+
const storageModule = await import('@/lib/server/storage')
|
|
87
|
+
const storage = storageModule.default || storageModule
|
|
88
|
+
storage.saveProviderConfigs({
|
|
89
|
+
'custom-llama-server': {
|
|
90
|
+
id: 'custom-llama-server',
|
|
91
|
+
name: 'llama-server',
|
|
92
|
+
type: 'custom',
|
|
93
|
+
baseUrl: 'http://127.0.0.1:8080/v1',
|
|
94
|
+
models: ['my-model'],
|
|
95
|
+
requiresApiKey: false,
|
|
96
|
+
credentialId: null,
|
|
97
|
+
isEnabled: true,
|
|
98
|
+
createdAt: 1,
|
|
99
|
+
updatedAt: 1,
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const providersModule = await import('@/lib/providers/index')
|
|
104
|
+
const providers = providersModule.default || providersModule
|
|
105
|
+
const resolved = providers.getProvider('custom-llama-server')
|
|
106
|
+
|
|
107
|
+
console.log(JSON.stringify({
|
|
108
|
+
defaultEndpoint: resolved?.defaultEndpoint ?? null,
|
|
109
|
+
optionalApiKey: resolved?.optionalApiKey ?? null,
|
|
110
|
+
requiresApiKey: resolved?.requiresApiKey ?? null,
|
|
111
|
+
}))
|
|
112
|
+
`)
|
|
113
|
+
|
|
114
|
+
assert.equal(output.defaultEndpoint, 'http://127.0.0.1:8080/v1')
|
|
115
|
+
assert.equal(output.optionalApiKey, true)
|
|
116
|
+
assert.equal(output.requiresApiKey, false)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('custom provider with uuid-style ID resolves correctly', () => {
|
|
120
|
+
const output = runWithTempDataDir<{
|
|
121
|
+
resolvedName: string | null
|
|
122
|
+
hasHandler: boolean
|
|
123
|
+
}>(`
|
|
124
|
+
const storageModule = await import('@/lib/server/storage')
|
|
125
|
+
const storage = storageModule.default || storageModule
|
|
126
|
+
storage.saveProviderConfigs({
|
|
127
|
+
'custom-d20b934e': {
|
|
128
|
+
id: 'custom-d20b934e',
|
|
129
|
+
name: 'My llama-server',
|
|
130
|
+
type: 'custom',
|
|
131
|
+
baseUrl: 'http://127.0.0.1:8080/v1',
|
|
132
|
+
models: ['llama-3.1-8b'],
|
|
133
|
+
requiresApiKey: false,
|
|
134
|
+
credentialId: null,
|
|
135
|
+
isEnabled: true,
|
|
136
|
+
createdAt: 1,
|
|
137
|
+
updatedAt: 1,
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const providersModule = await import('@/lib/providers/index')
|
|
142
|
+
const providers = providersModule.default || providersModule
|
|
143
|
+
const resolved = providers.getProvider('custom-d20b934e')
|
|
144
|
+
|
|
145
|
+
console.log(JSON.stringify({
|
|
146
|
+
resolvedName: resolved?.name ?? null,
|
|
147
|
+
hasHandler: typeof resolved?.handler?.streamChat === 'function',
|
|
148
|
+
}))
|
|
149
|
+
`)
|
|
150
|
+
|
|
151
|
+
assert.equal(output.resolvedName, 'My llama-server')
|
|
152
|
+
assert.equal(output.hasHandler, true)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('disabled custom providers are not resolved by getProvider', () => {
|
|
156
|
+
const output = runWithTempDataDir<{
|
|
157
|
+
resolved: boolean
|
|
158
|
+
}>(`
|
|
159
|
+
const storageModule = await import('@/lib/server/storage')
|
|
160
|
+
const storage = storageModule.default || storageModule
|
|
161
|
+
storage.saveProviderConfigs({
|
|
162
|
+
'custom-disabled': {
|
|
163
|
+
id: 'custom-disabled',
|
|
164
|
+
name: 'Disabled Provider',
|
|
165
|
+
type: 'custom',
|
|
166
|
+
baseUrl: 'http://127.0.0.1:8080/v1',
|
|
167
|
+
models: ['test'],
|
|
168
|
+
requiresApiKey: false,
|
|
169
|
+
credentialId: null,
|
|
170
|
+
isEnabled: false,
|
|
171
|
+
createdAt: 1,
|
|
172
|
+
updatedAt: 1,
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const providersModule = await import('@/lib/providers/index')
|
|
177
|
+
const providers = providersModule.default || providersModule
|
|
178
|
+
const resolved = providers.getProvider('custom-disabled')
|
|
179
|
+
|
|
180
|
+
console.log(JSON.stringify({
|
|
181
|
+
resolved: resolved !== null,
|
|
182
|
+
}))
|
|
183
|
+
`)
|
|
184
|
+
|
|
185
|
+
assert.equal(output.resolved, false)
|
|
186
|
+
})
|
|
@@ -286,7 +286,8 @@ function getCustomProviders(): Record<string, CustomProviderConfig> {
|
|
|
286
286
|
return Object.fromEntries(
|
|
287
287
|
Object.entries(configs).filter(([, config]) => config?.type === 'custom'),
|
|
288
288
|
)
|
|
289
|
-
} catch {
|
|
289
|
+
} catch (err) {
|
|
290
|
+
log.warn(TAG, 'Failed to load custom providers from storage', errorMessage(err))
|
|
290
291
|
return {}
|
|
291
292
|
}
|
|
292
293
|
}
|
|
@@ -325,6 +326,7 @@ export function getProviderList(): ProviderInfo[] {
|
|
|
325
326
|
defaultModels: c.models,
|
|
326
327
|
supportsModelDiscovery: false,
|
|
327
328
|
requiresApiKey: c.requiresApiKey,
|
|
329
|
+
optionalApiKey: !c.requiresApiKey,
|
|
328
330
|
requiresEndpoint: false as boolean,
|
|
329
331
|
defaultEndpoint: c.baseUrl,
|
|
330
332
|
}))
|
|
@@ -348,26 +350,47 @@ export function getProviderList(): ProviderInfo[] {
|
|
|
348
350
|
return [...builtins, ...customs, ...extensionProviders]
|
|
349
351
|
}
|
|
350
352
|
|
|
353
|
+
function buildCustomProviderConfig(custom: CustomProviderConfig): BuiltinProviderConfig {
|
|
354
|
+
return {
|
|
355
|
+
id: custom.id as ProviderId,
|
|
356
|
+
name: custom.name,
|
|
357
|
+
models: custom.models,
|
|
358
|
+
requiresApiKey: custom.requiresApiKey,
|
|
359
|
+
optionalApiKey: !custom.requiresApiKey,
|
|
360
|
+
requiresEndpoint: false,
|
|
361
|
+
defaultEndpoint: custom.baseUrl,
|
|
362
|
+
handler: {
|
|
363
|
+
streamChat: async (opts) => {
|
|
364
|
+
const patchedSession = { ...opts.session, apiEndpoint: custom.baseUrl }
|
|
365
|
+
const { streamOpenAiChat } = await import('./openai')
|
|
366
|
+
return streamOpenAiChat({ ...opts, session: patchedSession })
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
351
372
|
export function getProvider(id: string): BuiltinProviderConfig | null {
|
|
352
373
|
if (PROVIDERS[id]) return PROVIDERS[id]
|
|
353
|
-
|
|
374
|
+
|
|
354
375
|
// Check custom providers
|
|
355
376
|
const customs = getCustomProviders()
|
|
356
377
|
const custom = customs[id]
|
|
357
378
|
if (custom?.isEnabled) {
|
|
358
|
-
return
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
379
|
+
return buildCustomProviderConfig(custom)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Fallback: direct single-item DB lookup for custom-* providers
|
|
383
|
+
if (id.startsWith('custom-') && !custom) {
|
|
384
|
+
try {
|
|
385
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
386
|
+
const { loadStoredItem } = require('@/lib/server/storage') as typeof import('@/lib/server/storage')
|
|
387
|
+
const directConfig = loadStoredItem('provider_configs', id) as CustomProviderConfig | null
|
|
388
|
+
if (directConfig?.type === 'custom' && directConfig.isEnabled) {
|
|
389
|
+
log.info(TAG, `Resolved custom provider '${id}' via direct DB lookup (batch load missed it)`)
|
|
390
|
+
return buildCustomProviderConfig(directConfig)
|
|
391
|
+
}
|
|
392
|
+
} catch (err) {
|
|
393
|
+
log.warn(TAG, `Direct DB lookup failed for provider '${id}'`, errorMessage(err))
|
|
371
394
|
}
|
|
372
395
|
}
|
|
373
396
|
|