dsh-cait 1.0.3 → 1.0.4

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/lib/mt.js CHANGED
@@ -1,152 +1,152 @@
1
- export const MOCK_PROVIDER = 'mock'
2
- export const MOCK_MODEL = 'cait-mock'
3
- export const NO_ROUTE_MESSAGE = '未配置 DSH 模型路由,当前使用术语替换(离线)。'
4
- export const NO_MODEL_MESSAGE = '所选 DSH 模型路由没有可用模型,当前使用术语替换(离线)。'
5
-
6
- function service(ctx, name) {
7
- try {
8
- return typeof ctx?.get === 'function' ? ctx.get(name) : ctx?.[name]
9
- } catch {
10
- return undefined
11
- }
12
- }
13
-
14
- function configuredProviderIds(loader) {
15
- const ids = new Set()
16
- try {
17
- for (const entry of loader?.entries?.() ?? []) {
18
- const providers = entry?.options?.config?.providers
19
- if (!providers || typeof providers !== 'object' || Array.isArray(providers)) continue
20
- for (const id of Object.keys(providers)) ids.add(id)
21
- }
22
- } catch {
23
- // Loader discovery is best-effort; the llm adapter catalog remains usable.
24
- }
25
- return ids
26
- }
27
-
28
- function normalizeModels(catalog) {
29
- if (!Array.isArray(catalog)) return []
30
- const seen = new Set()
31
- const models = []
32
- for (const item of catalog) {
33
- const id = String(item?.id ?? '').trim()
34
- if (!id || seen.has(id)) continue
35
- seen.add(id)
36
- models.push({ id, name: String(item?.name ?? id).trim() || id })
37
- }
38
- return models
39
- }
40
-
41
- async function discoverRoutes(llm, loader) {
42
- if (!llm || typeof llm.listProviders !== 'function') {
43
- return { providers: [], fallback: true, message: NO_ROUTE_MESSAGE }
44
- }
45
-
46
- let adapters
47
- try {
48
- adapters = await llm.listProviders()
49
- } catch {
50
- adapters = []
51
- }
52
- if (!Array.isArray(adapters)) adapters = []
53
-
54
- const configured = configuredProviderIds(loader)
55
- const hasLoaderRoutes = adapters.some((adapter) => configured.has(String(adapter?.id ?? '')))
56
- const providers = []
57
- const seen = new Set()
58
-
59
- for (const adapter of adapters) {
60
- const id = String(adapter?.id ?? '').trim()
61
- if (!id || id === MOCK_PROVIDER || seen.has(id)) continue
62
- // When the loader exposes provider configs, use it to avoid listing adapters
63
- // that have no configured route. If loader discovery found nothing, trust the
64
- // adapter catalog (some providers are registered outside loader entries).
65
- if (hasLoaderRoutes && !configured.has(id)) continue
66
- seen.add(id)
67
-
68
- let models = []
69
- if (typeof llm.listModels === 'function') {
70
- try {
71
- models = normalizeModels(await llm.listModels(id))
72
- } catch {
73
- models = []
74
- }
75
- }
76
- providers.push({
77
- id,
78
- name: String(adapter?.name ?? id).trim() || id,
79
- models,
80
- })
81
- }
82
-
83
- return {
84
- providers,
85
- fallback: providers.length === 0,
86
- message: providers.length ? '' : NO_ROUTE_MESSAGE,
87
- }
88
- }
89
-
90
- export function createMtRuntime(ctx) {
91
- const llm = service(ctx, 'llm')
92
- const loader = service(ctx, 'loader')
93
- let cached = null
94
- let cachedAt = 0
95
-
96
- async function listRoutes({ refresh = false } = {}) {
97
- if (!refresh && cached && Date.now() - cachedAt < 30_000) return cached
98
- cached = await discoverRoutes(llm, loader)
99
- cachedAt = Date.now()
100
- return cached
101
- }
102
-
103
- return {
104
- available: Boolean(llm && typeof llm.stream === 'function'),
105
- listRoutes,
106
- async stream(options) {
107
- if (!llm || typeof llm.stream !== 'function') {
108
- const error = new Error('DSH llm service is not available')
109
- error.code = 'LLM_UNAVAILABLE'
110
- throw error
111
- }
112
- return llm.stream(options)
113
- },
114
- }
115
- }
116
-
117
- export async function collectStreamText(chunks) {
118
- if (!chunks || typeof chunks[Symbol.asyncIterator] !== 'function') {
119
- const error = new Error('DSH 模型路由没有返回可迭代的流')
120
- error.code = 'MT_STREAM_INVALID'
121
- throw error
122
- }
123
-
124
- let text = ''
125
- let finish = null
126
- for await (const chunk of chunks) {
127
- if (!chunk || typeof chunk !== 'object') continue
128
- if (chunk.type === 'text-delta') {
129
- text += String(chunk.text ?? '')
130
- } else if (chunk.type === 'finish') {
131
- finish = chunk
132
- break
133
- }
134
- }
135
-
136
- if (!finish) {
137
- const error = new Error('DSH 模型路由未返回完成事件')
138
- error.code = 'MT_STREAM_INCOMPLETE'
139
- throw error
140
- }
141
-
142
- const reason = finish.reason || {}
143
- if (reason.kind !== 'stop') {
144
- const failure = reason.failure || {}
145
- const error = new Error(failure.message || 'DSH 模型路由调用失败')
146
- error.code = failure.code || (reason.kind === 'aborted' ? 'MT_ABORTED' : 'MT_STREAM_FAILED')
147
- error.status = failure.status
148
- throw error
149
- }
150
-
151
- return text.trim()
152
- }
1
+ export const MOCK_PROVIDER = 'mock'
2
+ export const MOCK_MODEL = 'cait-mock'
3
+ export const NO_ROUTE_MESSAGE = '未配置 DSH 模型路由,当前使用术语替换(离线)。'
4
+ export const NO_MODEL_MESSAGE = '所选 DSH 模型路由没有可用模型,当前使用术语替换(离线)。'
5
+
6
+ function service(ctx, name) {
7
+ try {
8
+ return typeof ctx?.get === 'function' ? ctx.get(name) : ctx?.[name]
9
+ } catch {
10
+ return undefined
11
+ }
12
+ }
13
+
14
+ function configuredProviderIds(loader) {
15
+ const ids = new Set()
16
+ try {
17
+ for (const entry of loader?.entries?.() ?? []) {
18
+ const providers = entry?.options?.config?.providers
19
+ if (!providers || typeof providers !== 'object' || Array.isArray(providers)) continue
20
+ for (const id of Object.keys(providers)) ids.add(id)
21
+ }
22
+ } catch {
23
+ // Loader discovery is best-effort; the llm adapter catalog remains usable.
24
+ }
25
+ return ids
26
+ }
27
+
28
+ function normalizeModels(catalog) {
29
+ if (!Array.isArray(catalog)) return []
30
+ const seen = new Set()
31
+ const models = []
32
+ for (const item of catalog) {
33
+ const id = String(item?.id ?? '').trim()
34
+ if (!id || seen.has(id)) continue
35
+ seen.add(id)
36
+ models.push({ id, name: String(item?.name ?? id).trim() || id })
37
+ }
38
+ return models
39
+ }
40
+
41
+ async function discoverRoutes(llm, loader) {
42
+ if (!llm || typeof llm.listProviders !== 'function') {
43
+ return { providers: [], fallback: true, message: NO_ROUTE_MESSAGE }
44
+ }
45
+
46
+ let adapters
47
+ try {
48
+ adapters = await llm.listProviders()
49
+ } catch {
50
+ adapters = []
51
+ }
52
+ if (!Array.isArray(adapters)) adapters = []
53
+
54
+ const configured = configuredProviderIds(loader)
55
+ const hasLoaderRoutes = adapters.some((adapter) => configured.has(String(adapter?.id ?? '')))
56
+ const providers = []
57
+ const seen = new Set()
58
+
59
+ for (const adapter of adapters) {
60
+ const id = String(adapter?.id ?? '').trim()
61
+ if (!id || id === MOCK_PROVIDER || seen.has(id)) continue
62
+ // When the loader exposes provider configs, use it to avoid listing adapters
63
+ // that have no configured route. If loader discovery found nothing, trust the
64
+ // adapter catalog (some providers are registered outside loader entries).
65
+ if (hasLoaderRoutes && !configured.has(id)) continue
66
+ seen.add(id)
67
+
68
+ let models = []
69
+ if (typeof llm.listModels === 'function') {
70
+ try {
71
+ models = normalizeModels(await llm.listModels(id))
72
+ } catch {
73
+ models = []
74
+ }
75
+ }
76
+ providers.push({
77
+ id,
78
+ name: String(adapter?.name ?? id).trim() || id,
79
+ models,
80
+ })
81
+ }
82
+
83
+ return {
84
+ providers,
85
+ fallback: providers.length === 0,
86
+ message: providers.length ? '' : NO_ROUTE_MESSAGE,
87
+ }
88
+ }
89
+
90
+ export function createMtRuntime(ctx) {
91
+ const llm = service(ctx, 'llm')
92
+ const loader = service(ctx, 'loader')
93
+ let cached = null
94
+ let cachedAt = 0
95
+
96
+ async function listRoutes({ refresh = false } = {}) {
97
+ if (!refresh && cached && Date.now() - cachedAt < 30_000) return cached
98
+ cached = await discoverRoutes(llm, loader)
99
+ cachedAt = Date.now()
100
+ return cached
101
+ }
102
+
103
+ return {
104
+ available: Boolean(llm && typeof llm.stream === 'function'),
105
+ listRoutes,
106
+ async stream(options) {
107
+ if (!llm || typeof llm.stream !== 'function') {
108
+ const error = new Error('DSH llm service is not available')
109
+ error.code = 'LLM_UNAVAILABLE'
110
+ throw error
111
+ }
112
+ return llm.stream(options)
113
+ },
114
+ }
115
+ }
116
+
117
+ export async function collectStreamText(chunks) {
118
+ if (!chunks || typeof chunks[Symbol.asyncIterator] !== 'function') {
119
+ const error = new Error('DSH 模型路由没有返回可迭代的流')
120
+ error.code = 'MT_STREAM_INVALID'
121
+ throw error
122
+ }
123
+
124
+ let text = ''
125
+ let finish = null
126
+ for await (const chunk of chunks) {
127
+ if (!chunk || typeof chunk !== 'object') continue
128
+ if (chunk.type === 'text-delta') {
129
+ text += String(chunk.text ?? '')
130
+ } else if (chunk.type === 'finish') {
131
+ finish = chunk
132
+ break
133
+ }
134
+ }
135
+
136
+ if (!finish) {
137
+ const error = new Error('DSH 模型路由未返回完成事件')
138
+ error.code = 'MT_STREAM_INCOMPLETE'
139
+ throw error
140
+ }
141
+
142
+ const reason = finish.reason || {}
143
+ if (reason.kind !== 'stop') {
144
+ const failure = reason.failure || {}
145
+ const error = new Error(failure.message || 'DSH 模型路由调用失败')
146
+ error.code = failure.code || (reason.kind === 'aborted' ? 'MT_ABORTED' : 'MT_STREAM_FAILED')
147
+ error.status = failure.status
148
+ throw error
149
+ }
150
+
151
+ return text.trim()
152
+ }