@goodandready/dsh-moa 0.2.22 → 0.2.24

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/client.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // - Segmented preset selectors and count pills
7
7
  // - Refined temperature sliders and searchable model picker
8
8
  // - Telemetry summary grid fed by /dsh-moa/history
9
- // - Robust error boundary and settingsScope reactivity
9
+ // - Robust error boundary and configForms reactivity
10
10
  //
11
11
  // English is the canonical source language; the ru translation is provided
12
12
  // by the DSH translation plugin at runtime (no bundled ru duplicate).
@@ -1776,8 +1776,8 @@ window.__ModuleLoader__.load({
1776
1776
 
1777
1777
  const scope = React.useMemo(() => {
1778
1778
  if (!effectiveCtx) return undefined
1779
- const s = effectiveCtx.get ? effectiveCtx.get('settingsScope') : effectiveCtx.settingsScope
1780
- return s && typeof s.bind === 'function' ? s.bind({ namespace: NS }) : undefined
1779
+ const s = effectiveCtx.get ? effectiveCtx.get('configForms') : effectiveCtx.configForms
1780
+ return s && typeof s.get === 'function' ? s.get(NS) : undefined
1781
1781
  }, [effectiveCtx])
1782
1782
 
1783
1783
  const snapshot = React.useSyncExternalStore
@@ -2135,7 +2135,7 @@ window.__ModuleLoader__.load({
2135
2135
  )
2136
2136
  }
2137
2137
 
2138
- exports.inject = ['slots', 'locale', 'inputTriggers', 'settingsScope']
2138
+ exports.inject = ['slots', 'locale', 'inputTriggers', 'configForms']
2139
2139
  exports.apply = function apply(ctx) {
2140
2140
  rootCtx = ctx
2141
2141
 
package/lib/pricing.js CHANGED
@@ -28,6 +28,7 @@ export const DIRECT_VENDOR_RATES = {
28
28
  export const FALLBACK_RATES = { input: 0.50, output: 1.50, cacheHit: 0.15 }
29
29
 
30
30
  let memoryCatalog = null
31
+ let memoryCachePath = null
31
32
  let lastFetchedAt = 0
32
33
 
33
34
  /**
@@ -35,6 +36,7 @@ let lastFetchedAt = 0
35
36
  */
36
37
  export function resetMemoryCatalog() {
37
38
  memoryCatalog = null
39
+ memoryCachePath = null
38
40
  lastFetchedAt = 0
39
41
  }
40
42
 
@@ -42,13 +44,14 @@ export function resetMemoryCatalog() {
42
44
  * Load cached catalog from disk into memory.
43
45
  */
44
46
  export function loadCachedCatalog(cachePath = CACHE_FILE) {
45
- if (memoryCatalog) return memoryCatalog
47
+ if (memoryCatalog && memoryCachePath === cachePath) return memoryCatalog
46
48
  try {
47
49
  if (existsSync(cachePath)) {
48
50
  const raw = readFileSync(cachePath, 'utf8')
49
51
  const parsed = JSON.parse(raw)
50
52
  if (parsed && typeof parsed.models === 'object') {
51
53
  memoryCatalog = parsed.models
54
+ memoryCachePath = cachePath
52
55
  lastFetchedAt = parsed.updatedAt || 0
53
56
  return memoryCatalog
54
57
  }
@@ -57,6 +60,7 @@ export function loadCachedCatalog(cachePath = CACHE_FILE) {
57
60
  // Ignore read/parse errors
58
61
  }
59
62
  memoryCatalog = {}
63
+ memoryCachePath = cachePath
60
64
  return memoryCatalog
61
65
  }
62
66
 
@@ -154,20 +158,103 @@ export function resolveModelRates(slot = {}, customPrices = {}, cachePath = CACH
154
158
  if (catalog[fullKey]) return { ...catalog[fullKey] }
155
159
  if (catalog[model]) return { ...catalog[model] }
156
160
 
157
- for (const [catId, catRate] of Object.entries(catalog)) {
161
+ if (!model) return { ...FALLBACK_RATES }
162
+
163
+ const entries = Object.entries(catalog)
164
+ if (entries.length === 0) return { ...FALLBACK_RATES }
165
+
166
+ // 3a. Exact case-insensitive fullKey match
167
+ for (const [catId, catRate] of entries) {
158
168
  const catLower = catId.toLowerCase()
159
- if (catLower.endsWith(`/${model}`) || catLower === model) {
160
- return { ...catRate }
169
+ if (catLower === fullKey) return { ...catRate }
170
+ }
171
+
172
+ // 3b. Exact model match with provider matching
173
+ if (provider) {
174
+ for (const [catId, catRate] of entries) {
175
+ const catLower = catId.toLowerCase()
176
+ const slash = catLower.indexOf('/')
177
+ const catProv = slash !== -1 ? catLower.slice(0, slash) : ''
178
+ const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
179
+ if (catProv === provider && (catMod === model || catLower.endsWith(`/${model}`))) {
180
+ return { ...catRate }
181
+ }
161
182
  }
162
183
  }
163
184
 
164
- for (const [catId, catRate] of Object.entries(catalog)) {
185
+ // 3c. Exact model match across providers or suffix match
186
+ for (const [catId, catRate] of entries) {
165
187
  const catLower = catId.toLowerCase()
166
- if (model && (catLower.includes(model) || model.includes(catLower))) {
188
+ const slash = catLower.indexOf('/')
189
+ const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
190
+ if (catMod === model || catLower.endsWith(`/${model}`)) {
167
191
  return { ...catRate }
168
192
  }
169
193
  }
170
194
 
195
+ // 3d. Candidate resolution: prioritize same provider, select longest matching ID,
196
+ // and avoid bare substring collisions across distinct models with different rates.
197
+ let pool = entries
198
+ if (provider) {
199
+ const provEntries = entries.filter(([catId]) => {
200
+ const catLower = catId.toLowerCase()
201
+ const slash = catLower.indexOf('/')
202
+ return slash !== -1 && catLower.slice(0, slash) === provider
203
+ })
204
+ if (provEntries.length > 0) {
205
+ pool = provEntries
206
+ }
207
+ }
208
+
209
+ let bestCandidates = []
210
+ let bestScore = 0
211
+
212
+ for (const [catId, catRate] of pool) {
213
+ const catLower = catId.toLowerCase()
214
+ const slash = catLower.indexOf('/')
215
+ const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
216
+
217
+ let score = 0
218
+ if (model.startsWith(catMod + '-') || model.startsWith(catMod + ':') || model.startsWith(catMod + '.')) {
219
+ // Query model is a specific variant/revision of catalog model (e.g. model="claude-3-sonnet-20240229", catMod="claude-3-sonnet")
220
+ score = 10000 + catMod.length
221
+ } else if (catMod.startsWith(model + '-') || catMod.startsWith(model + ':') || catMod.startsWith(model + '.')) {
222
+ // Catalog model is a revision/variant of query model
223
+ score = 5000 + model.length
224
+ } else if (model.includes(catMod) && catMod.length >= 4) {
225
+ score = 2000 + catMod.length
226
+ } else if (catMod.includes(model) && model.length >= 4) {
227
+ score = 1000 + model.length
228
+ }
229
+
230
+ if (score > 0) {
231
+ if (score > bestScore) {
232
+ bestScore = score
233
+ bestCandidates = [{ catId, catRate, score, catMod }]
234
+ } else if (score === bestScore) {
235
+ bestCandidates.push({ catId, catRate, score, catMod })
236
+ }
237
+ }
238
+ }
239
+
240
+ if (bestCandidates.length === 1) {
241
+ return { ...bestCandidates[0].catRate }
242
+ }
243
+
244
+ if (bestCandidates.length > 1) {
245
+ const firstRate = bestCandidates[0].catRate
246
+ const allAgree = bestCandidates.every(c =>
247
+ c.catRate.input === firstRate.input &&
248
+ c.catRate.output === firstRate.output
249
+ )
250
+ if (allAgree) {
251
+ return { ...firstRate }
252
+ }
253
+ // Conflict between different models with different pricing sharing a substring:
254
+ // return safe fallback rate instead of assigning a wrong model's price.
255
+ return { ...FALLBACK_RATES }
256
+ }
257
+
171
258
  // 4. Fallback rate
172
259
  return { ...FALLBACK_RATES }
173
260
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-moa",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Mixture of Agents (MoA) plugin for DeepSeek Harness with /moa slash command",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -48,7 +48,8 @@
48
48
  "inject": [
49
49
  "@deepseek-ai/dsh-client-locale",
50
50
  "@deepseek-ai/dsh-client-ui-settings",
51
- "@deepseek-ai/dsh-client-ui-input-trigger"
51
+ "@deepseek-ai/dsh-client-ui-input-trigger",
52
+ "@deepseek-ai/dsh-client-ui-slots"
52
53
  ]
53
54
  }
54
55
  },