@huai_xin/dsh-model-picker 0.1.1 → 0.1.3
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 +71 -26
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -96,10 +96,15 @@ const INVALIDATION_EVENTS = Object.freeze([
|
|
|
96
96
|
* model's effort metadata *after* the model leaves the catalogue — so a level
|
|
97
97
|
* still shows its name while its model is gone.
|
|
98
98
|
*
|
|
99
|
-
* @param
|
|
99
|
+
* @param resolveRemote - returns the client remote face carrying `session.modelCatalog`.
|
|
100
|
+
* A THUNK, not the face itself: this catalogue is built in `apply`, where the
|
|
101
|
+
* `remote` service is often not visible yet (it arrives later in the client boot
|
|
102
|
+
* sequence). Capturing the value here froze `undefined` and every load then
|
|
103
|
+
* reported "modelCatalog is unavailable".
|
|
104
|
+
* @param onDiagnostic - optional sink for load-stage notes.
|
|
100
105
|
* @returns the catalogue controller.
|
|
101
106
|
*/
|
|
102
|
-
function createCatalog(
|
|
107
|
+
function createCatalog(resolveRemote, onDiagnostic) {
|
|
103
108
|
const store = createStore({ value: null, status: 'idle', error: null })
|
|
104
109
|
const reasoning = new Map()
|
|
105
110
|
// Bumped by every invalidation. A response that resolves under an older
|
|
@@ -112,6 +117,11 @@ function createCatalog(remote, onDiagnostic) {
|
|
|
112
117
|
try { onDiagnostic?.(message) } catch { /* diagnostics never break the load */ }
|
|
113
118
|
}
|
|
114
119
|
|
|
120
|
+
/** The remote face as it is right now, or undefined while the service is absent. */
|
|
121
|
+
const currentRemote = () => {
|
|
122
|
+
try { return typeof resolveRemote === 'function' ? resolveRemote() : resolveRemote } catch { return undefined }
|
|
123
|
+
}
|
|
124
|
+
|
|
115
125
|
const reasoningFor = (selection) => selection === null || selection === undefined
|
|
116
126
|
? undefined
|
|
117
127
|
: reasoning.get(JSON.stringify([selection.provider, selection.model]))
|
|
@@ -120,8 +130,10 @@ function createCatalog(remote, onDiagnostic) {
|
|
|
120
130
|
const state = store.getSnapshot()
|
|
121
131
|
if (state.status === 'ready' && state.value !== null) return Promise.resolve(state.value)
|
|
122
132
|
if (inflight !== undefined) return inflight
|
|
133
|
+
const remote = currentRemote()
|
|
123
134
|
const run = remote?.session?.modelCatalog
|
|
124
135
|
if (typeof run !== 'function') {
|
|
136
|
+
note('modelCatalog 不可用:remote 尚未就绪')
|
|
125
137
|
store.set({ value: state.value, status: 'error', error: 'modelCatalog is unavailable' })
|
|
126
138
|
return Promise.resolve(undefined)
|
|
127
139
|
}
|
|
@@ -491,10 +503,17 @@ function createDirectory(catalog, projectedFace, remote, sessionId) {
|
|
|
491
503
|
*
|
|
492
504
|
* @param catalog - the shared catalogue controller.
|
|
493
505
|
* @param sessions - the sessions service face.
|
|
494
|
-
* @param
|
|
506
|
+
* @param resolveRemote - returns the client remote face. A thunk for the same
|
|
507
|
+
* reason the catalogue takes one: the service is often not visible yet when
|
|
508
|
+
* this object is built, and capturing it froze `undefined`.
|
|
495
509
|
* @returns the service object with `directoryFor`.
|
|
496
510
|
*/
|
|
497
|
-
function createDirectoryService(catalog, sessions,
|
|
511
|
+
function createDirectoryService(catalog, sessions, resolveRemote) {
|
|
512
|
+
/** The remote face as it is right now, or undefined while the service is absent. */
|
|
513
|
+
const currentRemote = () => {
|
|
514
|
+
try { return typeof resolveRemote === 'function' ? resolveRemote() : resolveRemote } catch { return undefined }
|
|
515
|
+
}
|
|
516
|
+
|
|
498
517
|
/**
|
|
499
518
|
* Keyed by session id, not by binding object.
|
|
500
519
|
*
|
|
@@ -518,7 +537,7 @@ function createDirectoryService(catalog, sessions, remote) {
|
|
|
518
537
|
const projectedFace = (() => {
|
|
519
538
|
try { return binding?.session?.projections?.faceOf?.('modelSelection') } catch { return undefined }
|
|
520
539
|
})()
|
|
521
|
-
const directory = createDirectory(catalog, projectedFace,
|
|
540
|
+
const directory = createDirectory(catalog, projectedFace, currentRemote(), sessionId)
|
|
522
541
|
directories.set(key, directory)
|
|
523
542
|
return directory
|
|
524
543
|
}
|
|
@@ -599,7 +618,7 @@ const CSS = `
|
|
|
599
618
|
`
|
|
600
619
|
|
|
601
620
|
//# module: src/ui/dict.js
|
|
602
|
-
|
|
621
|
+
/**
|
|
603
622
|
* The picker's two dictionaries.
|
|
604
623
|
*
|
|
605
624
|
* Kept as a plain object rather than the Client locale service because the
|
|
@@ -1063,26 +1082,52 @@ window.__ModuleLoader__.load({
|
|
|
1063
1082
|
report(`apply 已调用(slots=${ctx.get('slots') === undefined ? '尚未就绪' : '就绪'})`)
|
|
1064
1083
|
|
|
1065
1084
|
/**
|
|
1066
|
-
* The catalogue
|
|
1067
|
-
*
|
|
1085
|
+
* The catalogue is built HERE, in `apply`, but it reads the `remote`
|
|
1086
|
+
* service LAZILY.
|
|
1087
|
+
*
|
|
1088
|
+
* Two facts pull in opposite directions and this is the seam between
|
|
1089
|
+
* them:
|
|
1068
1090
|
*
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1091
|
+
* - Event subscriptions must be installed from the plugin's own `apply`
|
|
1092
|
+
* fiber. The gateway registers a forwarded-event listener in the
|
|
1093
|
+
* CALLING fiber's private event domain, and a subscription made from a
|
|
1094
|
+
* `ctx.inject` callback lands in a different domain: `$on` returns a
|
|
1095
|
+
* disposer, so it looks successful, but the listener never fires.
|
|
1096
|
+
* - `remote` is frequently NOT visible yet when `apply` runs. Capturing
|
|
1097
|
+
* `ctx.get('remote')` here froze `undefined`, and every catalogue load
|
|
1098
|
+
* then answered "modelCatalog is unavailable".
|
|
1099
|
+
*
|
|
1100
|
+
* So the catalogue holds a resolver, not the face, and the subscription
|
|
1101
|
+
* is attached as soon as the service appears.
|
|
1077
1102
|
*/
|
|
1078
|
-
const
|
|
1103
|
+
const remoteFace = () => {
|
|
1079
1104
|
try { return ctx.get('remote') } catch { return undefined }
|
|
1080
|
-
}
|
|
1081
|
-
const catalog = createCatalog(
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1105
|
+
}
|
|
1106
|
+
const catalog = createCatalog(remoteFace, (message) => { report(message) })
|
|
1107
|
+
/**
|
|
1108
|
+
* Attach the invalidation listeners, retrying once the remote exists.
|
|
1109
|
+
*
|
|
1110
|
+
* `ctx.inject` runs the callback as soon as the named services are
|
|
1111
|
+
* visible, which is the point at which `$on` can be reached — and it runs
|
|
1112
|
+
* in this same `apply` fiber, so the subscription still lands in the
|
|
1113
|
+
* dispatching domain.
|
|
1114
|
+
*/
|
|
1115
|
+
let detachCatalog = () => {}
|
|
1116
|
+
const attachCatalog = () => {
|
|
1117
|
+
const remote = remoteFace()
|
|
1118
|
+
if (remote === undefined) return false
|
|
1119
|
+
detachCatalog = catalog.attach(remote, (event) => {
|
|
1120
|
+
report(`收到失效事件 ${event}`)
|
|
1121
|
+
})
|
|
1122
|
+
report(`失效订阅:已挂到 remote.$on`)
|
|
1123
|
+
return true
|
|
1124
|
+
}
|
|
1125
|
+
if (!attachCatalog()) {
|
|
1126
|
+
report('remote 尚未就绪,等它出现后再挂失效订阅')
|
|
1127
|
+
if (typeof ctx.inject === 'function') {
|
|
1128
|
+
ctx.inject(['remote'], () => { attachCatalog() })
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1086
1131
|
const detachReset = typeof ctx.on === 'function'
|
|
1087
1132
|
? ctx.on('connection/reset', () => { catalog.resetGeneration() })
|
|
1088
1133
|
: undefined
|
|
@@ -1094,7 +1139,7 @@ window.__ModuleLoader__.load({
|
|
|
1094
1139
|
})()
|
|
1095
1140
|
const directories = shipped !== undefined && typeof shipped.directoryFor === 'function'
|
|
1096
1141
|
? shipped
|
|
1097
|
-
: createDirectoryService(catalog, sessions,
|
|
1142
|
+
: createDirectoryService(catalog, sessions, remoteFace)
|
|
1098
1143
|
const ownsDirectories = directories !== shipped
|
|
1099
1144
|
report(`目录来源:${ownsDirectories ? '本插件自带实现' : '复用官方 modelDirectories'}`)
|
|
1100
1145
|
const detachDirectories = ownsDirectories && typeof ctx.provide === 'function'
|
|
@@ -1130,7 +1175,7 @@ window.__ModuleLoader__.load({
|
|
|
1130
1175
|
* picker degrades to its "could not load the model list" state rather
|
|
1131
1176
|
* than taking the whole slot down.
|
|
1132
1177
|
*/
|
|
1133
|
-
const remoteSession = at['remote.session'] ?? ctx.get('remote.session') ??
|
|
1178
|
+
const remoteSession = at['remote.session'] ?? ctx.get('remote.session') ?? remoteFace()?.session
|
|
1134
1179
|
|
|
1135
1180
|
slots.inject(SLOT, () => {
|
|
1136
1181
|
report('槽位回调已运行,准备注册 occupant')
|
|
@@ -1180,7 +1225,7 @@ window.__ModuleLoader__.load({
|
|
|
1180
1225
|
return {
|
|
1181
1226
|
sessionId,
|
|
1182
1227
|
localeId,
|
|
1183
|
-
remote,
|
|
1228
|
+
remote: remoteFace(),
|
|
1184
1229
|
remoteSession,
|
|
1185
1230
|
sessions: sessionSessions,
|
|
1186
1231
|
catalog,
|