@aomi-labs/react 0.3.14 → 0.3.16

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/dist/index.js CHANGED
@@ -90,6 +90,7 @@ var logThreadMetadataChange = (source, threadId, prev, next) => {
90
90
  function initThreadControl() {
91
91
  return {
92
92
  model: null,
93
+ modelMode: "auto",
93
94
  app: null,
94
95
  controlDirty: false,
95
96
  isProcessing: false
@@ -233,16 +234,36 @@ var ThreadStore = class {
233
234
  }
234
235
  };
235
236
 
237
+ // packages/react/src/utils/model-selection.ts
238
+ var PREFERRED_DEFAULT_MODEL_PATTERNS = [
239
+ /^claude-4\.5-haiku/i,
240
+ /^claude.*haiku/i,
241
+ /^gpt-4o-mini/i,
242
+ /^gemini.*flash/i
243
+ ];
244
+ function resolveAutoModel(models) {
245
+ var _a;
246
+ if (models.length === 0) return null;
247
+ for (const pattern of PREFERRED_DEFAULT_MODEL_PATTERNS) {
248
+ const match = models.find((model) => pattern.test(model));
249
+ if (match) return match;
250
+ }
251
+ return (_a = models[0]) != null ? _a : null;
252
+ }
253
+
236
254
  // packages/react/src/contexts/control-context.tsx
237
255
  import { jsx } from "react/jsx-runtime";
238
256
  var API_KEY_STORAGE_KEY = "aomi_api_key";
239
257
  var CLIENT_ID_STORAGE_KEY = "aomi_client_id";
240
258
  var PROVIDER_KEYS_STORAGE_KEY = "aomi_provider_keys";
259
+ var MODEL_SELECTION_STORAGE_KEY = "aomi_model_selection";
241
260
  var PROVIDER_KEY_SECRET_PREFIX = "PROVIDER_KEY:";
242
261
  function getOrCreateClientId() {
243
262
  var _a, _b, _c, _d, _e;
244
263
  try {
245
- const storedClientId = (_a = globalThis.localStorage) == null ? void 0 : _a.getItem(CLIENT_ID_STORAGE_KEY);
264
+ const storedClientId = (_a = globalThis.localStorage) == null ? void 0 : _a.getItem(
265
+ CLIENT_ID_STORAGE_KEY
266
+ );
246
267
  if (storedClientId && storedClientId.trim().length > 0) {
247
268
  return storedClientId;
248
269
  }
@@ -259,6 +280,49 @@ function getDefaultApp(apps) {
259
280
  var _a;
260
281
  return apps.includes("default") ? "default" : (_a = apps[0]) != null ? _a : null;
261
282
  }
283
+ function readStoredModelPreference() {
284
+ var _a;
285
+ try {
286
+ const raw = (_a = globalThis.localStorage) == null ? void 0 : _a.getItem(MODEL_SELECTION_STORAGE_KEY);
287
+ if (!raw) return { mode: "auto", model: null };
288
+ const parsed = JSON.parse(raw);
289
+ return {
290
+ mode: parsed.mode === "manual" ? "manual" : "auto",
291
+ model: typeof parsed.model === "string" ? parsed.model : null
292
+ };
293
+ } catch (e) {
294
+ return { mode: "auto", model: null };
295
+ }
296
+ }
297
+ function writeStoredModelPreference(preference) {
298
+ var _a;
299
+ try {
300
+ (_a = globalThis.localStorage) == null ? void 0 : _a.setItem(
301
+ MODEL_SELECTION_STORAGE_KEY,
302
+ JSON.stringify(preference)
303
+ );
304
+ } catch (e) {
305
+ }
306
+ }
307
+ function resolvePreferredModelSelection(preference, models, defaultModel) {
308
+ var _a;
309
+ if (preference.mode === "manual" && preference.model && models.includes(preference.model)) {
310
+ return preference;
311
+ }
312
+ if (preference.mode === "auto") {
313
+ return {
314
+ mode: "auto",
315
+ model: (_a = resolveAutoModel(models)) != null ? _a : defaultModel
316
+ };
317
+ }
318
+ return {
319
+ mode: "auto",
320
+ model: defaultModel != null ? defaultModel : resolveAutoModel(models)
321
+ };
322
+ }
323
+ function getFallbackModel(models, defaultModel) {
324
+ return defaultModel != null ? defaultModel : resolveAutoModel(models);
325
+ }
262
326
  function resolveAuthorizedApp(app, authorizedApps, defaultApp) {
263
327
  if (app && authorizedApps.includes(app)) {
264
328
  return app;
@@ -378,13 +442,10 @@ function ControlContextProvider({
378
442
  const fetchApps = async () => {
379
443
  var _a2;
380
444
  try {
381
- const apps = await aomiClientRef.current.getApps(
382
- sessionIdRef.current,
383
- {
384
- publicKey: publicKeyRef.current,
385
- apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
386
- }
387
- );
445
+ const apps = await aomiClientRef.current.getApps(sessionIdRef.current, {
446
+ publicKey: publicKeyRef.current,
447
+ apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
448
+ });
388
449
  const defaultApp = getDefaultApp(apps);
389
450
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
390
451
  authorizedApps: apps,
@@ -406,13 +467,10 @@ function ControlContextProvider({
406
467
  const models = await aomiClientRef.current.getModels(
407
468
  sessionIdRef.current
408
469
  );
409
- setStateInternal((prev) => {
410
- var _a2;
411
- return __spreadProps(__spreadValues({}, prev), {
412
- availableModels: models,
413
- defaultModel: (_a2 = models[0]) != null ? _a2 : null
414
- });
415
- });
470
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
471
+ availableModels: models,
472
+ defaultModel: resolveAutoModel(models)
473
+ }));
416
474
  } catch (error) {
417
475
  console.error("Failed to fetch models:", error);
418
476
  }
@@ -495,26 +553,20 @@ function ControlContextProvider({
495
553
  () => stateRef.current.providerKeys,
496
554
  []
497
555
  );
498
- const hasProviderKey = useCallback(
499
- (provider) => {
500
- const keys = stateRef.current.providerKeys;
501
- if (provider) return provider in keys;
502
- return Object.keys(keys).length > 0;
503
- },
504
- []
505
- );
556
+ const hasProviderKey = useCallback((provider) => {
557
+ const keys = stateRef.current.providerKeys;
558
+ if (provider) return provider in keys;
559
+ return Object.keys(keys).length > 0;
560
+ }, []);
506
561
  const getAvailableModels = useCallback(async () => {
507
562
  try {
508
563
  const models = await aomiClientRef.current.getModels(
509
564
  sessionIdRef.current
510
565
  );
511
- setStateInternal((prev) => {
512
- var _a2, _b2;
513
- return __spreadProps(__spreadValues({}, prev), {
514
- availableModels: models,
515
- defaultModel: (_b2 = (_a2 = prev.defaultModel) != null ? _a2 : models[0]) != null ? _b2 : null
516
- });
517
- });
566
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
567
+ availableModels: models,
568
+ defaultModel: resolveAutoModel(models)
569
+ }));
518
570
  return models;
519
571
  } catch (error) {
520
572
  console.error("Failed to fetch models:", error);
@@ -524,13 +576,10 @@ function ControlContextProvider({
524
576
  const getAuthorizedApps = useCallback(async () => {
525
577
  var _a2;
526
578
  try {
527
- const apps = await aomiClientRef.current.getApps(
528
- sessionIdRef.current,
529
- {
530
- publicKey: publicKeyRef.current,
531
- apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
532
- }
533
- );
579
+ const apps = await aomiClientRef.current.getApps(sessionIdRef.current, {
580
+ publicKey: publicKeyRef.current,
581
+ apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
582
+ });
534
583
  const defaultApp = getDefaultApp(apps);
535
584
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
536
585
  authorizedApps: apps,
@@ -551,6 +600,19 @@ function ControlContextProvider({
551
600
  const metadata = getThreadMetadataRef.current(sessionIdRef.current);
552
601
  return (_a2 = metadata == null ? void 0 : metadata.control) != null ? _a2 : initThreadControl();
553
602
  }, []);
603
+ const getPreferredThreadControl = useCallback(() => {
604
+ const preference = readStoredModelPreference();
605
+ const selection = resolvePreferredModelSelection(
606
+ preference,
607
+ stateRef.current.availableModels,
608
+ stateRef.current.defaultModel
609
+ );
610
+ return __spreadProps(__spreadValues({}, initThreadControl()), {
611
+ model: selection.model,
612
+ modelMode: selection.mode,
613
+ controlDirty: selection.model !== null
614
+ });
615
+ }, []);
554
616
  const getCurrentThreadApp = useCallback(() => {
555
617
  var _a2, _b2, _c;
556
618
  const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(sessionIdRef.current)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
@@ -560,60 +622,75 @@ function ControlContextProvider({
560
622
  stateRef.current.defaultApp
561
623
  )) != null ? _c : "default";
562
624
  }, []);
563
- const onModelSelect = useCallback(async (model) => {
564
- var _a2, _b2, _c, _d, _e;
565
- const threadId = sessionIdRef.current;
566
- const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
567
- const isProcessing2 = currentControl.isProcessing;
568
- console.log("[control-context] onModelSelect called", {
569
- model,
570
- isProcessing: isProcessing2,
571
- threadId
572
- });
573
- if (isProcessing2) {
574
- console.warn("[control-context] Cannot switch model while processing");
575
- return;
576
- }
577
- const app = (_c = resolveAuthorizedApp(
578
- currentControl.app,
579
- stateRef.current.authorizedApps,
580
- stateRef.current.defaultApp
581
- )) != null ? _c : "default";
582
- console.log("[control-context] onModelSelect updating metadata", {
583
- threadId,
584
- model,
585
- app,
586
- currentControl
587
- });
588
- updateThreadMetadataRef.current(threadId, {
589
- control: __spreadProps(__spreadValues({}, currentControl), {
625
+ const onModelSelect = useCallback(
626
+ async (model, options) => {
627
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
628
+ const threadId = sessionIdRef.current;
629
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
630
+ const isProcessing2 = currentControl.isProcessing;
631
+ const modelMode = (_c = options == null ? void 0 : options.mode) != null ? _c : "manual";
632
+ console.log("[control-context] onModelSelect called", {
633
+ model,
634
+ modelMode,
635
+ isProcessing: isProcessing2,
636
+ threadId
637
+ });
638
+ if (isProcessing2) {
639
+ console.warn("[control-context] Cannot switch model while processing");
640
+ return;
641
+ }
642
+ const app = (_d = resolveAuthorizedApp(
643
+ currentControl.app,
644
+ stateRef.current.authorizedApps,
645
+ stateRef.current.defaultApp
646
+ )) != null ? _d : "default";
647
+ console.log("[control-context] onModelSelect updating metadata", {
648
+ threadId,
590
649
  model,
591
650
  app,
592
- controlDirty: true
593
- })
594
- });
595
- console.log("[control-context] onModelSelect calling backend setModel", {
596
- threadId,
597
- model,
598
- app,
599
- backendUrl: aomiClientRef.current
600
- });
601
- try {
602
- const result = await aomiClientRef.current.setModel(
651
+ currentControl
652
+ });
653
+ updateThreadMetadataRef.current(threadId, {
654
+ control: __spreadProps(__spreadValues({}, currentControl), {
655
+ model,
656
+ modelMode,
657
+ app,
658
+ controlDirty: true
659
+ })
660
+ });
661
+ console.log("[control-context] onModelSelect calling backend setModel", {
603
662
  threadId,
604
663
  model,
605
- {
664
+ app,
665
+ backendUrl: aomiClientRef.current
666
+ });
667
+ try {
668
+ const result = await aomiClientRef.current.setModel(threadId, model, {
606
669
  app,
607
- apiKey: (_d = stateRef.current.apiKey) != null ? _d : void 0,
608
- clientId: (_e = stateRef.current.clientId) != null ? _e : void 0
670
+ apiKey: (_e = stateRef.current.apiKey) != null ? _e : void 0,
671
+ clientId: (_f = stateRef.current.clientId) != null ? _f : void 0
672
+ });
673
+ console.log("[control-context] onModelSelect backend result", result);
674
+ writeStoredModelPreference({
675
+ mode: modelMode,
676
+ model: modelMode === "manual" ? model : null
677
+ });
678
+ const latestControl = (_h = (_g = getThreadMetadataRef.current(threadId)) == null ? void 0 : _g.control) != null ? _h : currentControl;
679
+ if (latestControl.model === model && latestControl.app === app) {
680
+ updateThreadMetadataRef.current(threadId, {
681
+ control: __spreadProps(__spreadValues({}, latestControl), {
682
+ modelMode,
683
+ controlDirty: false
684
+ })
685
+ });
609
686
  }
610
- );
611
- console.log("[control-context] onModelSelect backend result", result);
612
- } catch (err) {
613
- console.error("[control-context] setModel failed:", err);
614
- throw err;
615
- }
616
- }, []);
687
+ } catch (err) {
688
+ console.error("[control-context] setModel failed:", err);
689
+ throw err;
690
+ }
691
+ },
692
+ []
693
+ );
617
694
  const onAppSelect = useCallback((app) => {
618
695
  var _a2, _b2;
619
696
  const threadId = sessionIdRef.current;
@@ -625,9 +702,7 @@ function ControlContextProvider({
625
702
  threadId
626
703
  });
627
704
  if (isProcessing2) {
628
- console.warn(
629
- "[control-context] Cannot switch app while processing"
630
- );
705
+ console.warn("[control-context] Cannot switch app while processing");
631
706
  return;
632
707
  }
633
708
  if (stateRef.current.authorizedApps.length > 0 && !stateRef.current.authorizedApps.includes(app)) {
@@ -659,6 +734,86 @@ function ControlContextProvider({
659
734
  });
660
735
  }
661
736
  }, []);
737
+ const syncCurrentThreadControl = useCallback(async () => {
738
+ var _a2, _b2, _c, _d, _e, _f, _g;
739
+ const threadId = sessionIdRef.current;
740
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
741
+ if (!currentControl.controlDirty || currentControl.isProcessing || !currentControl.model) {
742
+ return;
743
+ }
744
+ const app = (_c = resolveAuthorizedApp(
745
+ currentControl.app,
746
+ stateRef.current.authorizedApps,
747
+ stateRef.current.defaultApp
748
+ )) != null ? _c : "default";
749
+ await aomiClientRef.current.setModel(threadId, currentControl.model, {
750
+ app,
751
+ apiKey: (_d = stateRef.current.apiKey) != null ? _d : void 0,
752
+ clientId: (_e = stateRef.current.clientId) != null ? _e : void 0
753
+ });
754
+ const latestControl = (_g = (_f = getThreadMetadataRef.current(threadId)) == null ? void 0 : _f.control) != null ? _g : currentControl;
755
+ if (latestControl.model === currentControl.model && latestControl.app === currentControl.app) {
756
+ updateThreadMetadataRef.current(threadId, {
757
+ control: __spreadProps(__spreadValues({}, latestControl), {
758
+ app,
759
+ controlDirty: false
760
+ })
761
+ });
762
+ }
763
+ }, []);
764
+ useEffect(() => {
765
+ var _a2;
766
+ const threadId = sessionIdRef.current;
767
+ const metadata = getThreadMetadataRef.current(threadId);
768
+ if (!metadata || metadata.control.isProcessing) return;
769
+ const currentControl = metadata.control;
770
+ let nextControl = null;
771
+ if (currentControl.model === null) {
772
+ const preferred = getPreferredThreadControl();
773
+ if (!preferred.model) return;
774
+ nextControl = __spreadProps(__spreadValues({}, currentControl), {
775
+ model: preferred.model,
776
+ modelMode: preferred.modelMode,
777
+ controlDirty: true
778
+ });
779
+ } else if (state.availableModels.length > 0) {
780
+ const currentMode = (_a2 = currentControl.modelMode) != null ? _a2 : "manual";
781
+ if (currentMode === "auto") {
782
+ const autoModel = getFallbackModel(
783
+ state.availableModels,
784
+ state.defaultModel
785
+ );
786
+ if (autoModel && currentControl.model !== autoModel) {
787
+ nextControl = __spreadProps(__spreadValues({}, currentControl), {
788
+ model: autoModel,
789
+ modelMode: "auto",
790
+ controlDirty: true
791
+ });
792
+ }
793
+ } else if (!state.availableModels.includes(currentControl.model)) {
794
+ const fallbackModel = getFallbackModel(
795
+ state.availableModels,
796
+ state.defaultModel
797
+ );
798
+ if (fallbackModel) {
799
+ nextControl = __spreadProps(__spreadValues({}, currentControl), {
800
+ model: fallbackModel,
801
+ modelMode: "auto",
802
+ controlDirty: true
803
+ });
804
+ }
805
+ }
806
+ }
807
+ if (!nextControl) return;
808
+ updateThreadMetadataRef.current(threadId, {
809
+ control: nextControl
810
+ });
811
+ }, [
812
+ getPreferredThreadControl,
813
+ sessionId,
814
+ state.availableModels,
815
+ state.defaultModel
816
+ ]);
662
817
  const getControlState = useCallback(() => stateRef.current, []);
663
818
  const onControlStateChange = useCallback(
664
819
  (callback) => {
@@ -701,6 +856,8 @@ function ControlContextProvider({
701
856
  onAppSelect,
702
857
  isProcessing,
703
858
  markControlSynced,
859
+ syncCurrentThreadControl,
860
+ getPreferredThreadControl,
704
861
  getControlState,
705
862
  onControlStateChange,
706
863
  setState
@@ -1363,7 +1520,8 @@ function buildThreadLists(threadMetadata) {
1363
1520
  function buildThreadListAdapter({
1364
1521
  aomiClientRef,
1365
1522
  threadContext,
1366
- setIsRunning
1523
+ setIsRunning,
1524
+ getInitialControl = initThreadControl
1367
1525
  }) {
1368
1526
  const { regularThreads, archivedThreads } = buildThreadLists(
1369
1527
  threadContext.allThreadsMetadata
@@ -1379,7 +1537,7 @@ function buildThreadListAdapter({
1379
1537
  title: "New Chat",
1380
1538
  status: "regular",
1381
1539
  lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1382
- control: initThreadControl()
1540
+ control: getInitialControl()
1383
1541
  })
1384
1542
  );
1385
1543
  threadContext.setThreadMessages(threadId, []);
@@ -1451,7 +1609,7 @@ function buildThreadListAdapter({
1451
1609
  title: "New Chat",
1452
1610
  status: "regular",
1453
1611
  lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1454
- control: initThreadControl()
1612
+ control: getInitialControl()
1455
1613
  })
1456
1614
  );
1457
1615
  threadContext.setThreadMessages(defaultId, []);
@@ -1613,7 +1771,12 @@ function AomiRuntimeCore({
1613
1771
  const eventContext = useEventContext();
1614
1772
  const notificationContext = useNotification();
1615
1773
  const { user, onUserStateChange, getUserState } = useUser();
1616
- const { getControlState, getCurrentThreadApp } = useControl();
1774
+ const {
1775
+ getControlState,
1776
+ getCurrentThreadApp,
1777
+ getPreferredThreadControl,
1778
+ syncCurrentThreadControl
1779
+ } = useControl();
1617
1780
  const sessionManagerRef = useRef8(null);
1618
1781
  const walletHandler = useWalletHandler({
1619
1782
  getSession: () => {
@@ -1699,18 +1862,15 @@ function AomiRuntimeCore({
1699
1862
  [aomiClientRef, getUserState]
1700
1863
  );
1701
1864
  useEffect4(() => {
1702
- const unsubscribe = eventContext.subscribe(
1703
- "user_state_request",
1704
- () => {
1705
- var _a, _b, _c;
1706
- const session = (_b = (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadContext.currentThreadId)) != null ? _b : getSession(threadContext.currentThreadId);
1707
- eventContext.sendOutboundSystem({
1708
- type: "user_state_response",
1709
- sessionId: threadContext.currentThreadId,
1710
- payload: (_c = session.getUserState()) != null ? _c : getUserState()
1711
- });
1712
- }
1713
- );
1865
+ const unsubscribe = eventContext.subscribe("user_state_request", () => {
1866
+ var _a, _b, _c;
1867
+ const session = (_b = (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadContext.currentThreadId)) != null ? _b : getSession(threadContext.currentThreadId);
1868
+ eventContext.sendOutboundSystem({
1869
+ type: "user_state_response",
1870
+ sessionId: threadContext.currentThreadId,
1871
+ payload: (_c = session.getUserState()) != null ? _c : getUserState()
1872
+ });
1873
+ });
1714
1874
  return unsubscribe;
1715
1875
  }, [eventContext, threadContext.currentThreadId, getSession, getUserState]);
1716
1876
  useEffect4(() => {
@@ -1799,10 +1959,12 @@ function AomiRuntimeCore({
1799
1959
  () => buildThreadListAdapter({
1800
1960
  aomiClientRef,
1801
1961
  threadContext,
1802
- setIsRunning
1962
+ setIsRunning,
1963
+ getInitialControl: getPreferredThreadControl
1803
1964
  }),
1804
1965
  [
1805
1966
  aomiClientRef,
1967
+ getPreferredThreadControl,
1806
1968
  setIsRunning,
1807
1969
  threadContext,
1808
1970
  threadContext.currentThreadId,
@@ -1848,6 +2010,7 @@ function AomiRuntimeCore({
1848
2010
  (part) => part.type === "text"
1849
2011
  ).map((part) => part.text).join("\n");
1850
2012
  if (text) {
2013
+ await syncCurrentThreadControl();
1851
2014
  await orchestratorSendMessage(text, threadContext.currentThreadId);
1852
2015
  }
1853
2016
  },
@@ -1865,9 +2028,14 @@ function AomiRuntimeCore({
1865
2028
  const userContext = useUser();
1866
2029
  const sendMessage = useCallback7(
1867
2030
  async (text) => {
2031
+ await syncCurrentThreadControl();
1868
2032
  await orchestratorSendMessage(text, threadContext.currentThreadId);
1869
2033
  },
1870
- [orchestratorSendMessage, threadContext.currentThreadId]
2034
+ [
2035
+ orchestratorSendMessage,
2036
+ syncCurrentThreadControl,
2037
+ threadContext.currentThreadId
2038
+ ]
1871
2039
  );
1872
2040
  const cancelGeneration = useCallback7(() => {
1873
2041
  void orchestratorCancel(threadContext.currentThreadId);
@@ -2101,6 +2269,7 @@ export {
2101
2269
  initThreadControl,
2102
2270
  normalizeSimulatedFee,
2103
2271
  parseChainId,
2272
+ resolveAutoModel,
2104
2273
  toAAWalletCall,
2105
2274
  toAAWalletCalls,
2106
2275
  toViemSignTypedDataArgs,