diff --git a/src/apps/settings-modal/SettingsModal.tsx b/src/apps/settings-modal/SettingsModal.tsx index 1086724f4..28749de7d 100644 --- a/src/apps/settings-modal/SettingsModal.tsx +++ b/src/apps/settings-modal/SettingsModal.tsx @@ -199,7 +199,7 @@ export function SettingsModal(props: { const { setTab } = props; const isToolsTab = props.tab === 'tools'; - const enableAixDebugger = Is.Deployment.Localhost; + const enableAixDebuggerDialog = true; const handleSetTab = React.useCallback((_event: any, value: string | number | null) => { setTab((value ?? undefined) as PreferencesTabId); @@ -221,12 +221,12 @@ export function SettingsModal(props: { {!isMobile && !isToolsTab && } + {isToolsTab && } {isToolsTab && } - {isToolsTab && } } sx={_styles.modal} diff --git a/src/common/stores/store-ui.ts b/src/common/stores/store-ui.ts index de6c33c2f..ff6d67bcd 100644 --- a/src/common/stores/store-ui.ts +++ b/src/common/stores/store-ui.ts @@ -4,6 +4,7 @@ import { persist } from 'zustand/middleware'; import type { ContentScaling, UIComplexityMode } from '~/common/app.theme'; import { BrowserLang } from '~/common/util/pwaUtils'; +import { Release } from '~/common/app.release'; // UI Preferences @@ -47,6 +48,11 @@ interface UIPreferencesStore { composerQuickButton: 'off' | 'call' | 'beam'; setComposerQuickButton: (composerQuickButton: 'off' | 'call' | 'beam') => void; + // Advanced features + + aixInspector: boolean; + toggleAixInspector: () => void; + // UI Dismissals dismissals: Record; @@ -103,6 +109,11 @@ export const useUIPreferencesStore = create()( composerQuickButton: 'beam', setComposerQuickButton: (composerQuickButton: 'off' | 'call' | 'beam') => set({ composerQuickButton }), + // Advanced features + + aixInspector: false, + toggleAixInspector: () => set((state) => ({ aixInspector: !state.aixInspector })), + // UI Dismissals dismissals: {}, @@ -133,6 +144,13 @@ export const useUIPreferencesStore = create()( */ version: 3, + partialize: (state) => { + if (Release.IsNodeDevBuild) return state; // in dev, persist everything + // In production, exclude aixInspector from persistence + const { aixInspector, ...rest } = state; + return rest; + }, + migrate: (state: any, fromVersion: number): UIPreferencesStore => { // 1: rename 'enterToSend' to 'enterIsNewline' (flip the meaning) @@ -169,6 +187,10 @@ export function useUIContentScaling(): ContentScaling { return useUIPreferencesStore((state) => state.contentScaling); } +export function getAixInspector(): boolean { + return useUIPreferencesStore.getState().aixInspector; +} + export function useUIIsDismissed(key: string | null): boolean | undefined { return useUIPreferencesStore((state) => !key ? undefined : Boolean(state.dismissals[key])); diff --git a/src/modules/aix/client/aix.client.ts b/src/modules/aix/client/aix.client.ts index a9a67082d..7cc07a5e8 100644 --- a/src/modules/aix/client/aix.client.ts +++ b/src/modules/aix/client/aix.client.ts @@ -10,7 +10,8 @@ import { DMetricsChatGenerate_Lg, metricsChatGenerateLgToMd, metricsComputeChatG import { DModelParameterValues, getAllModelParameterValues } from '~/common/stores/llms/llms.parameters'; import { createErrorContentFragment, DMessageContentFragment, DMessageErrorPart, DMessageVoidFragment, isContentFragment, isErrorPart } from '~/common/stores/chat/chat.fragments'; import { findLLMOrThrow } from '~/common/stores/llms/store-llms'; -import { getLabsDevMode, getLabsDevNoStreaming } from '~/common/stores/store-ux-labs'; +import { getAixInspector } from '~/common/stores/store-ui'; +import { getLabsDevNoStreaming } from '~/common/stores/store-ux-labs'; import { metricsStoreAddChatGenerate } from '~/common/stores/metrics/store-metrics'; import { presentErrorToHumans } from '~/common/util/errorUtils'; import { webGeolocationCached } from '~/common/util/webGeolocationUtils'; @@ -631,7 +632,7 @@ async function _aixChatGenerateContent_LL( * - AIX inspector is now independent from sudo mode * - every request thereafter both sends back the Aix server-side dispatch packet, and appends all the particles received by the client side */ - const requestServerDebugging = getLabsDevMode(); + const requestServerDebugging = getAixInspector(); const debugContext = !requestServerDebugging ? undefined : { contextName: aixContext.name, contextRef: aixContext.ref }; /** diff --git a/src/modules/aix/client/debugger/AixDebuggerDialog.tsx b/src/modules/aix/client/debugger/AixDebuggerDialog.tsx index 32398aca3..861b9780b 100644 --- a/src/modules/aix/client/debugger/AixDebuggerDialog.tsx +++ b/src/modules/aix/client/debugger/AixDebuggerDialog.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; import { useShallow } from 'zustand/react/shallow'; -import { Box, Button, Divider, FormControl, FormLabel, Option, Select, Typography } from '@mui/joy'; +import { Box, Button, Divider, FormControl, FormLabel, Link, Option, Select, Switch, Typography } from '@mui/joy'; import ClearAllIcon from '@mui/icons-material/ClearAll'; import { GoodModal } from '~/common/components/modals/GoodModal'; import { useIsMobile } from '~/common/components/useMatchMedia'; +import { useUIPreferencesStore } from '~/common/stores/store-ui'; import { AixDebuggerFrame } from './AixDebuggerFrame'; import { aixClientDebuggerActions, useAixClientDebuggerStore } from './memstore-aix-client-debugger'; @@ -15,8 +16,10 @@ export function AixDebuggerDialog(props: { onClose: () => void; }) { - // external state - we subscribe to Any update - it's a temp debugger anyway + // external state const isMobile = useIsMobile(); + const aixInspector = useUIPreferencesStore(state => state.aixInspector); + // NOTE: we subscribe to Any update - which can be ultra noisy const { frames, activeFrameId, maxFrames } = useAixClientDebuggerStore(useShallow((state) => ({ frames: state.frames, activeFrameId: state.activeFrameId, @@ -42,7 +45,14 @@ export function AixDebuggerDialog(props: { + } autoOverflow fullscreen={isMobile} sx={{ maxWidth: undefined }} @@ -74,7 +84,7 @@ export function AixDebuggerDialog(props: { - {/* History Size Preferenes */} + {/* History Size Preferences */} History Size @@ -111,11 +121,20 @@ export function AixDebuggerDialog(props: { {!frames.length && <> - No AIX API requests recorded yet + {aixInspector ? 'Ready to capture' : 'AI Request Inspector'} - - Ensure AIX debugging is active (Settings -> Labs -> Developer Mode) - and you are running your own localhost:3000 installation. + + {aixInspector + ? 'Your next AI request will be captured here.' + : <> + + Turn on inspector + to see the exact requests to AI models. + } } {!activeFrame && !!frames.length && ( diff --git a/src/modules/aix/client/debugger/AixDebuggerFrame.tsx b/src/modules/aix/client/debugger/AixDebuggerFrame.tsx index 9cd45b768..2c6247248 100644 --- a/src/modules/aix/client/debugger/AixDebuggerFrame.tsx +++ b/src/modules/aix/client/debugger/AixDebuggerFrame.tsx @@ -94,8 +94,9 @@ export function AixDebuggerFrame(props: { {/* Body */} - - -> Body {frame.bodySize > 0 && `(${frame.bodySize.toLocaleString()} chars)`} + + -> Body + {frame.bodySize > 0 && {frame.bodySize.toLocaleString()} bytes}