@dbx-tools/ui-mastra 0.3.1 → 0.3.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/package.json CHANGED
@@ -26,19 +26,19 @@
26
26
  "shiki": "^3.0.0",
27
27
  "sql-formatter": "^15.6.9",
28
28
  "streamdown": "^2.5.0",
29
- "@dbx-tools/shared-core": "0.3.1",
30
- "@dbx-tools/shared-genie": "0.3.1",
31
- "@dbx-tools/shared-model": "0.3.1",
32
- "@dbx-tools/shared-mastra": "0.3.1",
33
- "@dbx-tools/ui-appkit": "0.3.1",
34
- "@dbx-tools/ui-branding": "0.3.1"
29
+ "@dbx-tools/shared-genie": "0.3.3",
30
+ "@dbx-tools/shared-core": "0.3.3",
31
+ "@dbx-tools/shared-mastra": "0.3.3",
32
+ "@dbx-tools/shared-model": "0.3.3",
33
+ "@dbx-tools/ui-appkit": "0.3.3",
34
+ "@dbx-tools/ui-branding": "0.3.3"
35
35
  },
36
36
  "main": "index.ts",
37
37
  "license": "UNLICENSED",
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "version": "0.3.1",
41
+ "version": "0.3.3",
42
42
  "types": "index.ts",
43
43
  "type": "module",
44
44
  "exports": {
@@ -100,6 +100,7 @@ export const ChatView = ({
100
100
  models,
101
101
  model,
102
102
  onModelChange,
103
+ defaultModelName,
103
104
  onLoadMore,
104
105
  isLoadingMore = false,
105
106
  hasMore = false,
@@ -285,11 +286,25 @@ export const ChatView = ({
285
286
  // otherwise as static text.
286
287
  const showModelDisplay = Boolean(onModelChange);
287
288
  const modelChangeable = Boolean(models && models.length > 0);
288
- // Label the current model by its human-readable name when the catalogue
289
- // carries one (`displayName`), falling back to the raw endpoint id, then
290
- // to "Server default" when no model is pinned.
291
- const currentModelLabel =
292
- models?.find((m) => m.name === model)?.displayName || model || "Server default";
289
+ // Human-readable name for an endpoint id, using the catalogue's
290
+ // `displayName` when present, else the raw id.
291
+ const modelLabel = (name?: string): string | undefined => {
292
+ if (!name) return undefined;
293
+ return models?.find((m) => m.name === name)?.displayName || name;
294
+ };
295
+ // The default option shows the server's fallback model by name when known
296
+ // (from `defaultModelName`), else a neutral "Default" - no "server default"
297
+ // phrasing either way.
298
+ const defaultOptionLabel = modelLabel(defaultModelName) ?? "Default";
299
+ // Label the current model by its human-readable name when a model is
300
+ // pinned, else the default-option label.
301
+ const currentModelLabel = modelLabel(model) || defaultOptionLabel;
302
+ // Picker entries sorted by their human-readable label (case-insensitive).
303
+ const sortedModels = [...(models ?? [])].sort((a, b) =>
304
+ (a.displayName || a.name).localeCompare(b.displayName || b.name, undefined, {
305
+ sensitivity: "base",
306
+ }),
307
+ );
293
308
  const showClear = Boolean(onClear);
294
309
  const showExport = Boolean(onExportConversation);
295
310
  // The conversation sidebar turns on once the host wires both the
@@ -680,11 +695,13 @@ export const ChatView = ({
680
695
  size="sm"
681
696
  className="h-7 w-auto max-w-[200px] gap-1 rounded-full px-2.5 text-xs [&_svg]:size-3"
682
697
  >
683
- <SelectValue placeholder="Server default" />
698
+ <SelectValue placeholder={defaultOptionLabel} />
684
699
  </SelectTrigger>
685
700
  <SelectContent>
686
- <SelectItem value={DEFAULT_MODEL_VALUE}>Server default</SelectItem>
687
- {models!.map((m) => (
701
+ <SelectItem value={DEFAULT_MODEL_VALUE}>
702
+ {defaultOptionLabel}
703
+ </SelectItem>
704
+ {sortedModels.map((m) => (
688
705
  <SelectItem key={m.name} value={m.name}>
689
706
  {m.displayName || m.name}
690
707
  </SelectItem>
@@ -15,6 +15,7 @@ export {
15
15
  useChartFetch,
16
16
  useMastraClient,
17
17
  useMastraConfig,
18
+ useMastraDefaultModel,
18
19
  useMastraModels,
19
20
  useMastraSuggestions,
20
21
  useMastraThreads,
@@ -10,6 +10,7 @@ import { exportChat, type EmbedResolver, type ExportFormat } from "../support/ex
10
10
  import { useBrand } from "@dbx-tools/ui-branding/react";
11
11
  import {
12
12
  useMastraClient,
13
+ useMastraDefaultModel,
13
14
  useMastraModels,
14
15
  useMastraSuggestions,
15
16
  useMastraThreads,
@@ -347,6 +348,10 @@ export const useMastraChat = (
347
348
  // hidden and skips the catalogue fetch entirely.
348
349
  const showModelPicker = Boolean(options.showModelPicker);
349
350
  const { models } = useMastraModels(showModelPicker);
351
+ // The endpoint the active agent falls back to when no model is pinned, so
352
+ // the picker can name its "Server default" option. Fetched only when the
353
+ // picker is shown; `null` when the agent's model is dynamic.
354
+ const defaultModelName = useMastraDefaultModel(agentId, showModelPicker);
350
355
  // Starter suggestions: an explicit `options.suggestions` always
351
356
  // wins (including `[]` to force none) and is rendered verbatim;
352
357
  // otherwise auto-source the agent's Genie space sample questions.
@@ -1333,6 +1338,7 @@ export const useMastraChat = (
1333
1338
  models: showModelPicker ? models : undefined,
1334
1339
  model,
1335
1340
  onModelChange: showModelPicker ? setModel : undefined,
1341
+ defaultModelName: showModelPicker ? (defaultModelName ?? undefined) : undefined,
1336
1342
  onLoadMore: loadOlderHistory,
1337
1343
  isLoadingMore,
1338
1344
  hasMore: activeSession.hasMoreHistory,
@@ -116,6 +116,13 @@ export type ChatViewProps = {
116
116
  /** Currently selected model name; empty string means "use server default". */
117
117
  model?: string;
118
118
  onModelChange?: (model: string) => void;
119
+ /**
120
+ * The serving-endpoint id the server falls back to when no model is pinned.
121
+ * When set, the picker's "Server default" option names it (e.g. "Server
122
+ * default (Claude Sonnet 4.6)"), using the matching `models` entry's
123
+ * `displayName` when available.
124
+ */
125
+ defaultModelName?: string;
119
126
  /**
120
127
  * Optional infinite-scroll-up handler. Fired when the user scrolls
121
128
  * within `TOP_LOAD_MORE_THRESHOLD_PX` of the top of the
@@ -174,6 +174,24 @@ export class MastraPluginClient extends MastraClient {
174
174
  return payload.endpoints;
175
175
  }
176
176
 
177
+ /**
178
+ * Fetch the static default serving-endpoint id `agentId` falls back to
179
+ * when no model is pinned, from `GET ${basePath}/default-model`. Returns
180
+ * `null` when the agent resolves its model dynamically at call time (or the
181
+ * agent id is unknown) - there's nothing static to advertise. The picker
182
+ * uses this to name its "Server default" option. Defaults to the server's
183
+ * default agent when `agentId` is omitted.
184
+ */
185
+ async defaultModel(agentId?: string, signal?: AbortSignal): Promise<string | null> {
186
+ const query = agentId ? `?agentId=${encodeURIComponent(agentId)}` : "";
187
+ const payload = await this.#getJson(
188
+ `${this.basePath}${routes.MASTRA_ROUTES.defaultModel}${query}`,
189
+ wire.DefaultModelResponseSchema,
190
+ signal,
191
+ );
192
+ return payload.model;
193
+ }
194
+
177
195
  /**
178
196
  * Fetch the curated starter questions for `agentId`'s Genie space
179
197
  * from `GET ${basePath}/suggestions`. Empty when the agent has no
@@ -533,6 +551,38 @@ export const useMastraModels = (
533
551
  return { models, loading, error };
534
552
  };
535
553
 
554
+ /**
555
+ * Fetch the static default serving-endpoint id the given agent (or the
556
+ * server's default agent) falls back to when no model is pinned, via
557
+ * `client.defaultModel(agentId)`. `null` while loading, when the agent's
558
+ * model is dynamic, or on error - the picker then shows a plain "Server
559
+ * default". Pass `enabled: false` to skip the fetch (e.g. picker hidden).
560
+ */
561
+ export const useMastraDefaultModel = (
562
+ agentId?: string,
563
+ enabled = true,
564
+ ): string | null => {
565
+ const client = useMastraClient();
566
+ const [model, setModel] = useState<string | null>(null);
567
+
568
+ useEffect(() => {
569
+ if (!enabled) return;
570
+ const controller = new AbortController();
571
+ client
572
+ .defaultModel(agentId, controller.signal)
573
+ .then((m) => {
574
+ if (!controller.signal.aborted) setModel(m);
575
+ })
576
+ .catch(() => {
577
+ // Non-critical: a missing default just yields a plain "Server default".
578
+ if (!controller.signal.aborted) setModel(null);
579
+ });
580
+ return () => controller.abort();
581
+ }, [client, agentId, enabled]);
582
+
583
+ return model;
584
+ };
585
+
536
586
  /**
537
587
  * Fetch the curated starter questions for an agent's Genie space via
538
588
  * `client.suggestions(agentId)`. These are the `sample_questions` an