@ai-wendao/ant 0.4.11 → 0.4.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/client.js +93 -39
- package/conversation.js +11 -4
- package/index.js +11 -3
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -6,6 +6,7 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
const { createElement, useEffect, useRef, useState } = require('react')
|
|
7
7
|
const h = createElement
|
|
8
8
|
const SERVICE_URL_STORAGE_KEY = 'ant-team-service-url'
|
|
9
|
+
const PROVIDER_ROUTES_STORAGE_KEY = 'ant-team-provider-routes'
|
|
9
10
|
const DEVICE_ID_STORAGE_KEY = 'ant-team-device-id'
|
|
10
11
|
const DEFAULT_MODEL_INITIALIZED_KEY = 'ant-team-default-model-initialized'
|
|
11
12
|
const ANT_BUNDLE_PACKAGE_NAME = '@ai-wendao/ant'
|
|
@@ -1989,9 +1990,12 @@ window.__ModuleLoader__.load({
|
|
|
1989
1990
|
return status
|
|
1990
1991
|
}
|
|
1991
1992
|
|
|
1992
|
-
const modelProvider = (model) => model.protocol === 'anthropic'
|
|
1993
|
-
|
|
1994
|
-
|
|
1993
|
+
const modelProvider = (model) => model.protocol === 'anthropic'
|
|
1994
|
+
? model.provider_id === 'anthropic-default' || !model.provider_id ? 'ant-team-anthropic' : `ant-team-anthropic-${model.provider_id}`
|
|
1995
|
+
: model.provider_id === 'openai-default' || !model.provider_id
|
|
1996
|
+
? model.openai_api_mode === 'responses' ? 'ant-team-openai-responses' : 'ant-team-openai-chat-completions'
|
|
1997
|
+
: `ant-team-openai-${model.provider_id}`
|
|
1998
|
+
const modelSelectionKey = (model) => JSON.stringify({ provider_id: model.provider_id || '', protocol: model.protocol, model_id: model.model_id })
|
|
1995
1999
|
const defaultPreferenceKey = (rawServiceUrl) => `${DEFAULT_MODEL_INITIALIZED_KEY}:${normalizeServiceUrl(rawServiceUrl)}:${savedDeviceId() || ''}`
|
|
1996
2000
|
const readDefaultPreference = (rawServiceUrl) => {
|
|
1997
2001
|
try { return JSON.parse(localStorage.getItem(defaultPreferenceKey(rawServiceUrl)) || 'null') } catch { return null }
|
|
@@ -2003,9 +2007,9 @@ window.__ModuleLoader__.load({
|
|
|
2003
2007
|
if (!selection) return null
|
|
2004
2008
|
const exact = models.find((model) => model.model_id === selection.model && modelProvider(model) === selection.provider)
|
|
2005
2009
|
if (exact) return exact
|
|
2006
|
-
const
|
|
2007
|
-
? 'openai' : selection.provider === 'ant-team-anthropic' ? 'anthropic' : null
|
|
2008
|
-
return
|
|
2010
|
+
const legacyProvider = ['ant-team-openai', 'ant-team-openai-chat-completions', 'ant-team-openai-responses'].includes(selection.provider)
|
|
2011
|
+
? 'openai-default' : selection.provider === 'ant-team-anthropic' ? 'anthropic-default' : null
|
|
2012
|
+
return legacyProvider ? models.find((model) => model.provider_id === legacyProvider && model.model_id === selection.model) : null
|
|
2009
2013
|
}
|
|
2010
2014
|
const readDshDefaultSelection = async () => {
|
|
2011
2015
|
if (typeof remote?.settings?.describe !== 'function') throw new Error('当前 DSH 未提供全局默认模型读取接口。')
|
|
@@ -2051,34 +2055,69 @@ window.__ModuleLoader__.load({
|
|
|
2051
2055
|
const normalized = normalizeServiceUrl(rawServiceUrl)
|
|
2052
2056
|
if (!normalized || !Array.isArray(models)) throw new Error('ANT 返回的团队模型目录格式无效。')
|
|
2053
2057
|
if (typeof remote?.settings?.mutate !== 'function') throw new Error('当前 DSH 版本未提供模型设置接口。')
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2058
|
+
const grouped = new Map()
|
|
2059
|
+
for (const model of models) {
|
|
2060
|
+
const protocol = model.protocol
|
|
2061
|
+
if (!['openai', 'anthropic'].includes(protocol)) continue
|
|
2062
|
+
const providerId = model.provider_id || (protocol === 'openai' ? 'openai-default' : 'anthropic-default')
|
|
2063
|
+
const key = `${providerId}\u0000${protocol}`
|
|
2064
|
+
let provider = grouped.get(key)
|
|
2065
|
+
if (!provider) {
|
|
2066
|
+
provider = {
|
|
2067
|
+
providerId,
|
|
2068
|
+
providerName: model.provider_name || (protocol === 'openai'
|
|
2069
|
+
? `ANT 团队 · OpenAI · ${model.openai_api_mode === 'responses' ? 'Responses' : 'Chat Completions'}`
|
|
2070
|
+
: 'ANT 团队 · Anthropic'),
|
|
2071
|
+
protocol,
|
|
2072
|
+
apiMode: model.openai_api_mode === 'responses' ? 'responses' : 'chat_completions',
|
|
2073
|
+
legacyEndpoint: typeof model.provider_id !== 'string' || !model.provider_id,
|
|
2074
|
+
models: [],
|
|
2075
|
+
}
|
|
2076
|
+
grouped.set(key, provider)
|
|
2067
2077
|
}
|
|
2078
|
+
provider.models.push(model)
|
|
2079
|
+
}
|
|
2080
|
+
const providers = [...grouped.values()]
|
|
2081
|
+
const routeForProvider = (provider) => provider.providerId === 'openai-default'
|
|
2082
|
+
? provider.apiMode === 'responses' ? 'ant-team-openai-responses' : 'ant-team-openai-chat-completions'
|
|
2083
|
+
: provider.providerId === 'anthropic-default'
|
|
2084
|
+
? 'ant-team-anthropic' : `ant-team-${provider.protocol}-${provider.providerId}`
|
|
2085
|
+
const routes = providers.map(routeForProvider)
|
|
2086
|
+
const ops = []
|
|
2087
|
+
let previousRoutes = []
|
|
2088
|
+
try {
|
|
2089
|
+
const parsed = JSON.parse(localStorage.getItem(PROVIDER_ROUTES_STORAGE_KEY) || '[]')
|
|
2090
|
+
if (Array.isArray(parsed)) previousRoutes = parsed.filter((route) => typeof route === 'string')
|
|
2091
|
+
} catch {}
|
|
2092
|
+
const knownRoutes = new Set([...previousRoutes, 'ant-team-openai', 'ant-team-openai-chat-completions',
|
|
2093
|
+
'ant-team-openai-responses', 'ant-team-anthropic'])
|
|
2094
|
+
for (const route of knownRoutes) {
|
|
2095
|
+
if (!routes.includes(route)) ops.push({ op: 'unset', path: ['providers', route] })
|
|
2096
|
+
}
|
|
2097
|
+
for (const provider of providers) {
|
|
2098
|
+
const route = routeForProvider(provider)
|
|
2099
|
+
const api = provider.protocol === 'openai'
|
|
2100
|
+
? provider.apiMode === 'responses' ? 'openai-responses' : 'openai-completions'
|
|
2101
|
+
: 'anthropic-messages'
|
|
2102
|
+
const baseURL = provider.legacyEndpoint
|
|
2103
|
+
? new URL(provider.protocol === 'openai' ? '/model-gateway/openai/v1' : '/model-gateway/anthropic', normalized).toString()
|
|
2104
|
+
: provider.protocol === 'openai'
|
|
2105
|
+
? new URL(`/model-gateway/openai/providers/${encodeURIComponent(provider.providerId)}/v1`, normalized).toString()
|
|
2106
|
+
: new URL(`/model-gateway/anthropic/providers/${encodeURIComponent(provider.providerId)}`, normalized).toString()
|
|
2068
2107
|
ops.push({ op: 'set', path: ['providers', route], value: {
|
|
2069
|
-
displayName:
|
|
2108
|
+
displayName: provider.providerName,
|
|
2070
2109
|
apiKeyEnv: 'ANT_MODEL_API_KEY',
|
|
2071
|
-
api
|
|
2072
|
-
baseURL
|
|
2073
|
-
|
|
2074
|
-
: new URL('/model-gateway/anthropic', normalized).toString(),
|
|
2075
|
-
models: protocolModels.map((model) => ({ id: model.model_id, name: model.display_name })),
|
|
2110
|
+
api,
|
|
2111
|
+
baseURL,
|
|
2112
|
+
models: provider.models.map((model) => ({ id: model.model_id, name: model.display_name })),
|
|
2076
2113
|
} })
|
|
2077
2114
|
}
|
|
2078
2115
|
const settingsWrite = await remote.settings.mutate('llm-pi-ai', ops, undefined)
|
|
2079
2116
|
if (!settingsWrite.ok) throw new Error(settingsWrite.error?.message || 'DSH 无法更新团队模型设置。')
|
|
2080
|
-
|
|
2081
|
-
|
|
2117
|
+
try { localStorage.setItem(PROVIDER_ROUTES_STORAGE_KEY, JSON.stringify(routes)) } catch {}
|
|
2118
|
+
const defaultMatch = rawDefaultModel && models.find((model) =>
|
|
2119
|
+
(rawDefaultModel.provider_id ? model.provider_id === rawDefaultModel.provider_id : model.protocol === rawDefaultModel.protocol)
|
|
2120
|
+
&& model.protocol === rawDefaultModel.protocol && model.model_id === rawDefaultModel.model_id)
|
|
2082
2121
|
const serverDefault = defaultMatch || null
|
|
2083
2122
|
const initialDefault = defaultMatch || (rawDefaultModel == null ? models[0] : null)
|
|
2084
2123
|
setServerDefaultModel(serverDefault)
|
|
@@ -2138,7 +2177,9 @@ window.__ModuleLoader__.load({
|
|
|
2138
2177
|
setModelMessage('正在从 ANT 刷新获准模型…')
|
|
2139
2178
|
try {
|
|
2140
2179
|
await requireCompatibleService(serviceUrl, true)
|
|
2141
|
-
const response = await fetch('/api/ant-teams/models', {
|
|
2180
|
+
const response = await fetch('/api/ant-teams/models', {
|
|
2181
|
+
method: 'POST', headers: { 'x-ant-model-provider-config': '1' },
|
|
2182
|
+
})
|
|
2142
2183
|
const result = await readLocalJsonResponse(response)
|
|
2143
2184
|
if (!response.ok) {
|
|
2144
2185
|
if (response.status === 401) setNeedsReconnect(true)
|
|
@@ -2327,13 +2368,26 @@ window.__ModuleLoader__.load({
|
|
|
2327
2368
|
], undefined)
|
|
2328
2369
|
if (!reset.ok) throw new Error(reset.error?.message || 'DSH 全局默认模型未能恢复。')
|
|
2329
2370
|
}
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2371
|
+
let managedRoutes = []
|
|
2372
|
+
try {
|
|
2373
|
+
const parsed = JSON.parse(localStorage.getItem(PROVIDER_ROUTES_STORAGE_KEY) || '[]')
|
|
2374
|
+
if (Array.isArray(parsed)) managedRoutes = parsed.filter((route) => typeof route === 'string')
|
|
2375
|
+
} catch {}
|
|
2376
|
+
if (typeof remote?.settings?.describe === 'function') {
|
|
2377
|
+
const described = await remote.settings.describe()
|
|
2378
|
+
if (!described?.ok) throw new Error(described?.error?.message || '无法读取 DSH 中的 ANT 提供方列表。')
|
|
2379
|
+
const providers = described.value?.namespaces?.find((item) => item.ns === 'llm-pi-ai')?.value?.providers
|
|
2380
|
+
for (const [route, provider] of Object.entries(providers || {})) {
|
|
2381
|
+
if (route.startsWith('ant-team-') && provider?.apiKeyEnv === 'ANT_MODEL_API_KEY') managedRoutes.push(route)
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
const routes = new Set([...managedRoutes, 'ant-team-openai-chat-completions', 'ant-team-openai-responses',
|
|
2385
|
+
'ant-team-openai', 'ant-team-anthropic'])
|
|
2386
|
+
const settingsResult = await remote.settings.mutate('llm-pi-ai', [...routes].map((route) => ({
|
|
2387
|
+
op: 'unset', path: ['providers', route],
|
|
2388
|
+
})), undefined)
|
|
2336
2389
|
if (!settingsResult.ok) throw new Error(settingsResult.error?.message || 'ANT 模型设置未能清除。')
|
|
2390
|
+
try { localStorage.removeItem(PROVIDER_ROUTES_STORAGE_KEY) } catch {}
|
|
2337
2391
|
if (typeof remote?.credentials?.unset !== 'function') throw new Error('DSH 未提供凭证删除接口。')
|
|
2338
2392
|
const credentialResult = await remote.credentials.unset('ANT_MODEL_API_KEY')
|
|
2339
2393
|
if (!credentialResult.ok) throw new Error(credentialResult.error?.message || 'ANT 成员凭证未能清除。')
|
|
@@ -2500,7 +2554,7 @@ window.__ModuleLoader__.load({
|
|
|
2500
2554
|
localStorage.setItem(SERVICE_URL_STORAGE_KEY, normalized)
|
|
2501
2555
|
const response = await fetch('/api/ant-teams/connect', {
|
|
2502
2556
|
method: 'POST',
|
|
2503
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2557
|
+
headers: { 'Content-Type': 'application/json', 'x-ant-model-provider-config': '1' },
|
|
2504
2558
|
body: JSON.stringify({
|
|
2505
2559
|
service_url: normalized,
|
|
2506
2560
|
email: normalizedEmail,
|
|
@@ -2551,13 +2605,13 @@ window.__ModuleLoader__.load({
|
|
|
2551
2605
|
const updateGuideUrl = pinnedServiceUrl
|
|
2552
2606
|
? new URL('/ant-update', pinnedServiceUrl).href : null
|
|
2553
2607
|
const currentDshDefaultModel = modelForProviderSelection(dshDefaultSelection, authorizedModels)
|
|
2554
|
-
const modelRows = authorizedModels.map((model) => h('div', { key: `${model.protocol}:${model.model_id}`, role: 'listitem',
|
|
2608
|
+
const modelRows = authorizedModels.map((model) => h('div', { key: `${model.provider_id || ''}:${model.protocol}:${model.model_id}`, role: 'listitem',
|
|
2555
2609
|
style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(120px, auto) minmax(130px, auto)',
|
|
2556
2610
|
gap: 12, alignItems: 'center', padding: '10px 2px', borderBottom: '1px solid var(--dsw-alias-border-l2)' } },
|
|
2557
|
-
h('strong', { style: { minWidth: 0, overflowWrap: 'anywhere' } }, model.display_name || model.model_id),
|
|
2611
|
+
h('strong', { style: { minWidth: 0, overflowWrap: 'anywhere' } }, `${model.provider_name ? `${model.provider_name} · ` : ''}${model.display_name || model.model_id}`),
|
|
2558
2612
|
h('span', { style: { display: 'inline-flex', justifySelf: 'start', padding: '3px 8px', borderRadius: 999,
|
|
2559
2613
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)', color: 'var(--dsw-alias-label-secondary)', fontSize: 12 } },
|
|
2560
|
-
model.protocol === 'anthropic' ? 'Anthropic Messages' : `OpenAI · ${
|
|
2614
|
+
model.protocol === 'anthropic' ? 'Anthropic Messages' : `OpenAI · ${model.openai_api_mode === 'responses' ? 'Responses' : 'Chat Completions'}`),
|
|
2561
2615
|
h('span', { style: { ...mutedTextStyle, textAlign: 'right' } }, '已同步到 DSH')))
|
|
2562
2616
|
const connectionPanel = h('section', { style: { ...settingsPanelStyle, display: activeTab === 'connection' ? 'grid' : 'none' } },
|
|
2563
2617
|
h('div', null,
|
|
@@ -2634,7 +2688,7 @@ window.__ModuleLoader__.load({
|
|
|
2634
2688
|
onChange: (event) => setDefaultModelDraft(event.target.value), style: inputStyle },
|
|
2635
2689
|
h('option', { value: '' }, '选择获准模型'),
|
|
2636
2690
|
...authorizedModels.map((model) => h('option', { key: modelSelectionKey(model), value: modelSelectionKey(model) },
|
|
2637
|
-
`${model.display_name || model.model_id} · ${model.protocol === 'anthropic' ? 'Anthropic' :
|
|
2691
|
+
`${model.provider_name ? `${model.provider_name} · ` : ''}${model.display_name || model.model_id} · ${model.protocol === 'anthropic' ? 'Anthropic' : model.openai_api_mode === 'responses' ? 'OpenAI Responses' : 'OpenAI Chat Completions'}`)))),
|
|
2638
2692
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' } },
|
|
2639
2693
|
h('button', { type: 'button', disabled: defaultModelBusy || !defaultModelDraft,
|
|
2640
2694
|
onClick: () => { const chosen = authorizedModels.find((model) => modelSelectionKey(model) === defaultModelDraft); void applyUserDefaultModel(chosen) },
|
package/conversation.js
CHANGED
|
@@ -167,8 +167,14 @@ async function prompt(ctx, sessionId, content, signal) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
export function teamModelProvider(model) {
|
|
170
|
-
if (model.protocol === 'anthropic')
|
|
171
|
-
|
|
170
|
+
if (model.protocol === 'anthropic') {
|
|
171
|
+
return !model.provider_id || model.provider_id === 'anthropic-default'
|
|
172
|
+
? 'ant-team-anthropic' : `ant-team-anthropic-${model.provider_id}`
|
|
173
|
+
}
|
|
174
|
+
if (!model.provider_id || model.provider_id === 'openai-default') {
|
|
175
|
+
return model.openai_api_mode === 'responses' ? 'ant-team-openai-responses' : 'ant-team-openai-chat-completions'
|
|
176
|
+
}
|
|
177
|
+
return `ant-team-openai-${model.provider_id}`
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
function dshDefaultSelection(ctx) {
|
|
@@ -182,8 +188,9 @@ export async function modelFor(ctx, memberJson, connection) {
|
|
|
182
188
|
const selection = dshDefaultSelection(ctx)
|
|
183
189
|
const localDefault = selection && models.find((item) => item.model_id === selection.model
|
|
184
190
|
&& teamModelProvider(item) === selection.provider)
|
|
185
|
-
const serverDefault = catalog.default_model && models.find((item) =>
|
|
186
|
-
|
|
191
|
+
const serverDefault = catalog.default_model && models.find((item) =>
|
|
192
|
+
(catalog.default_model.provider_id ? item.provider_id === catalog.default_model.provider_id : item.protocol === catalog.default_model.protocol)
|
|
193
|
+
&& item.protocol === catalog.default_model.protocol && item.model_id === catalog.default_model.model_id)
|
|
187
194
|
const model = localDefault || serverDefault || (!Object.hasOwn(catalog, 'default_model') ? models[0] : null)
|
|
188
195
|
if (!model && models.length) throw new Error('当前没有可用的 ANT 默认模型;请在插件“团队模型”中选择本机默认,或联系管理员检查授权。')
|
|
189
196
|
if (!model) throw new Error('当前没有可用的团队模型。')
|
package/index.js
CHANGED
|
@@ -69,8 +69,10 @@ function modelServiceBinding(settings) {
|
|
|
69
69
|
let url
|
|
70
70
|
try { url = new URL(provider.baseURL) } catch { return { configured, serviceUrl: null } }
|
|
71
71
|
const localHttp = url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)
|
|
72
|
-
|
|
73
|
-
||
|
|
72
|
+
const knownRoute = ['/model-gateway/openai/v1', '/model-gateway/anthropic'].includes(url.pathname)
|
|
73
|
+
|| /^\/model-gateway\/openai\/providers\/[A-Za-z0-9-]{1,64}\/v1$/u.test(url.pathname)
|
|
74
|
+
|| /^\/model-gateway\/anthropic\/providers\/[A-Za-z0-9-]{1,64}$/u.test(url.pathname)
|
|
75
|
+
if ((url.protocol !== 'https:' && !localHttp) || url.username || url.password || url.search || url.hash || !knownRoute) {
|
|
74
76
|
return { configured, serviceUrl: null }
|
|
75
77
|
}
|
|
76
78
|
origins.add(url.origin)
|
|
@@ -178,7 +180,11 @@ async function fetchMemberModels(request, credentials, settings) {
|
|
|
178
180
|
}
|
|
179
181
|
if (!session.ok) return jsonResponse(502, { message: '无法激活 ANT 模型会话。' })
|
|
180
182
|
const upstream = await fetch(new URL('/api/me/models', serviceUrl), {
|
|
181
|
-
headers: {
|
|
183
|
+
headers: {
|
|
184
|
+
authorization: `Bearer ${token}`,
|
|
185
|
+
accept: 'application/json',
|
|
186
|
+
...(request.headers.get('x-ant-model-provider-config') === '1' ? { 'x-ant-model-provider-config': '1' } : {}),
|
|
187
|
+
},
|
|
182
188
|
redirect: 'manual',
|
|
183
189
|
signal: AbortSignal.timeout(10_000),
|
|
184
190
|
})
|
|
@@ -865,6 +871,8 @@ async function fetchAuthRequest(request, endpoint, credentials, settings) {
|
|
|
865
871
|
accept: 'application/json',
|
|
866
872
|
'content-type': 'application/json',
|
|
867
873
|
origin: serviceUrl,
|
|
874
|
+
...(endpoint === 'dsh-connect' && request.headers.get('x-ant-model-provider-config') === '1'
|
|
875
|
+
? { 'x-ant-model-provider-config': '1' } : {}),
|
|
868
876
|
},
|
|
869
877
|
body: JSON.stringify(payload),
|
|
870
878
|
redirect: 'manual',
|