Skip to content

Commit 63cb4fb

Browse files
authored
fix: assistant with last used and fix metadata (#5955)
* fix: assistant with last used and fix metadata * chore: revert instruction and desc * chore: fix current assistant state * chore: updae metadata message assistant * chore: update test case
1 parent 160d158 commit 63cb4fb

File tree

8 files changed

+115
-26
lines changed

8 files changed

+115
-26
lines changed

web-app/src/constants/localStorage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ export const localStorageKey = {
1818
toolAvailability: 'tool-availability',
1919
mcpGlobalPermissions: 'mcp-global-permissions',
2020
lastUsedModel: 'last-used-model',
21+
lastUsedAssistant: 'last-used-assistant',
2122
}

web-app/src/hooks/__tests__/useAppState.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ vi.mock('../useAssistant', () => ({
1111
})),
1212
{
1313
getState: vi.fn(() => ({
14-
currentAssistant: { id: 'test-assistant', name: 'Test Assistant' }
14+
currentAssistant: { id: 'test-assistant', name: 'Test Assistant' },
15+
assistants: [{ id: 'test-assistant', name: 'Test Assistant' }]
1516
}))
1617
}
1718
)

web-app/src/hooks/__tests__/useChat.test.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,31 @@ vi.mock('../usePrompt', () => ({
1111
}))
1212

1313
vi.mock('../useAppState', () => ({
14-
useAppState: vi.fn(() => ({
15-
tools: [],
16-
updateTokenSpeed: vi.fn(),
17-
resetTokenSpeed: vi.fn(),
18-
updateTools: vi.fn(),
19-
updateStreamingContent: vi.fn(),
20-
updateLoadingModel: vi.fn(),
21-
setAbortController: vi.fn(),
22-
})),
14+
useAppState: Object.assign(
15+
vi.fn(() => ({
16+
tools: [],
17+
updateTokenSpeed: vi.fn(),
18+
resetTokenSpeed: vi.fn(),
19+
updateTools: vi.fn(),
20+
updateStreamingContent: vi.fn(),
21+
updateLoadingModel: vi.fn(),
22+
setAbortController: vi.fn(),
23+
})),
24+
{
25+
getState: vi.fn(() => ({
26+
tokenSpeed: { tokensPerSecond: 10 },
27+
}))
28+
}
29+
),
2330
}))
2431

2532
vi.mock('../useAssistant', () => ({
2633
useAssistant: vi.fn(() => ({
34+
assistants: [{
35+
id: 'test-assistant',
36+
instructions: 'test instructions',
37+
parameters: { stream: true },
38+
}],
2739
currentAssistant: {
2840
id: 'test-assistant',
2941
instructions: 'test instructions',
@@ -88,6 +100,12 @@ vi.mock('../useModelContextApproval', () => ({
88100
})),
89101
}))
90102

103+
vi.mock('../useModelLoad', () => ({
104+
useModelLoad: vi.fn(() => ({
105+
setModelLoadError: vi.fn(),
106+
})),
107+
}))
108+
91109
vi.mock('@tanstack/react-router', () => ({
92110
useRouter: vi.fn(() => ({
93111
navigate: vi.fn(),
@@ -96,6 +114,7 @@ vi.mock('@tanstack/react-router', () => ({
96114

97115
vi.mock('@/lib/completion', () => ({
98116
emptyThreadContent: { thread_id: 'test-thread', content: '' },
117+
extractToolCall: vi.fn(),
99118
newUserThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'user message' })),
100119
newAssistantThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'assistant message' })),
101120
sendCompletion: vi.fn(),

web-app/src/hooks/useAppState.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,20 @@ export const useAppState = create<AppState>()((set) => ({
3535
tokenSpeed: undefined,
3636
currentToolCall: undefined,
3737
updateStreamingContent: (content: ThreadMessage | undefined) => {
38+
const assistants = useAssistant.getState().assistants
39+
const currentAssistant = useAssistant.getState().currentAssistant
40+
41+
const selectedAssistant =
42+
assistants.find((a) => a.id === currentAssistant.id) || assistants[0]
43+
3844
set(() => ({
3945
streamingContent: content
4046
? {
4147
...content,
4248
created_at: content.created_at || Date.now(),
4349
metadata: {
4450
...content.metadata,
45-
assistant: useAssistant.getState().currentAssistant,
51+
assistant: selectedAssistant,
4652
},
4753
}
4854
: undefined,

web-app/src/hooks/useAssistant.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
import { createAssistant, deleteAssistant } from '@/services/assistants'
22
import { Assistant as CoreAssistant } from '@janhq/core'
33
import { create } from 'zustand'
4+
import { localStorageKey } from '@/constants/localStorage'
45

56
interface AssistantState {
67
assistants: Assistant[]
78
currentAssistant: Assistant
89
addAssistant: (assistant: Assistant) => void
910
updateAssistant: (assistant: Assistant) => void
1011
deleteAssistant: (id: string) => void
11-
setCurrentAssistant: (assistant: Assistant) => void
12+
setCurrentAssistant: (assistant: Assistant, saveToStorage?: boolean) => void
1213
setAssistants: (assistants: Assistant[]) => void
14+
getLastUsedAssistant: () => string | null
15+
setLastUsedAssistant: (assistantId: string) => void
16+
initializeWithLastUsed: () => void
17+
}
18+
19+
// Helper functions for localStorage
20+
const getLastUsedAssistantId = (): string | null => {
21+
try {
22+
return localStorage.getItem(localStorageKey.lastUsedAssistant)
23+
} catch (error) {
24+
console.debug('Failed to get last used assistant from localStorage:', error)
25+
return null
26+
}
27+
}
28+
29+
const setLastUsedAssistantId = (assistantId: string) => {
30+
try {
31+
localStorage.setItem(localStorageKey.lastUsedAssistant, assistantId)
32+
} catch (error) {
33+
console.debug('Failed to set last used assistant in localStorage:', error)
34+
}
1335
}
1436

1537
export const defaultAssistant: Assistant = {
@@ -51,17 +73,52 @@ export const useAssistant = create<AssistantState>()((set, get) => ({
5173
})
5274
},
5375
deleteAssistant: (id) => {
76+
const state = get()
5477
deleteAssistant(
55-
get().assistants.find((e) => e.id === id) as unknown as CoreAssistant
78+
state.assistants.find((e) => e.id === id) as unknown as CoreAssistant
5679
).catch((error) => {
5780
console.error('Failed to delete assistant:', error)
5881
})
59-
set({ assistants: get().assistants.filter((a) => a.id !== id) })
82+
83+
// Check if we're deleting the current assistant
84+
const wasCurrentAssistant = state.currentAssistant.id === id
85+
86+
set({ assistants: state.assistants.filter((a) => a.id !== id) })
87+
88+
// If the deleted assistant was current, fallback to default and update localStorage
89+
if (wasCurrentAssistant) {
90+
set({ currentAssistant: defaultAssistant })
91+
setLastUsedAssistantId(defaultAssistant.id)
92+
}
6093
},
61-
setCurrentAssistant: (assistant) => {
94+
setCurrentAssistant: (assistant, saveToStorage = true) => {
6295
set({ currentAssistant: assistant })
96+
if (saveToStorage) {
97+
setLastUsedAssistantId(assistant.id)
98+
}
6399
},
64100
setAssistants: (assistants) => {
65101
set({ assistants })
66102
},
103+
getLastUsedAssistant: () => {
104+
return getLastUsedAssistantId()
105+
},
106+
setLastUsedAssistant: (assistantId) => {
107+
setLastUsedAssistantId(assistantId)
108+
},
109+
initializeWithLastUsed: () => {
110+
const lastUsedId = getLastUsedAssistantId()
111+
if (lastUsedId) {
112+
const lastUsedAssistant = get().assistants.find(
113+
(a) => a.id === lastUsedId
114+
)
115+
if (lastUsedAssistant) {
116+
set({ currentAssistant: lastUsedAssistant })
117+
} else {
118+
// Fallback to default if last used assistant was deleted
119+
set({ currentAssistant: defaultAssistant })
120+
setLastUsedAssistantId(defaultAssistant.id)
121+
}
122+
}
123+
},
67124
}))

web-app/src/hooks/useChat.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const useChat = () => {
4545
updateLoadingModel,
4646
setAbortController,
4747
} = useAppState()
48-
const { currentAssistant } = useAssistant()
48+
const { assistants, currentAssistant } = useAssistant()
4949
const { updateProvider } = useModelProvider()
5050

5151
const { approvedTools, showApprovalModal, allowAllMCPPermissions } =
@@ -74,6 +74,9 @@ export const useChat = () => {
7474
return provider?.provider || selectedProvider
7575
}, [provider, selectedProvider])
7676

77+
const selectedAssistant =
78+
assistants.find((a) => a.id === currentAssistant.id) || assistants[0]
79+
7780
useEffect(() => {
7881
function setTools() {
7982
getTools().then((data: MCPTool[]) => {
@@ -92,14 +95,15 @@ export const useChat = () => {
9295

9396
const getCurrentThread = useCallback(async () => {
9497
let currentThread = retrieveThread()
98+
9599
if (!currentThread) {
96100
currentThread = await createThread(
97101
{
98102
id: selectedModel?.id ?? defaultModel(selectedProvider),
99103
provider: selectedProvider,
100104
},
101105
prompt,
102-
currentAssistant
106+
selectedAssistant
103107
)
104108
router.navigate({
105109
to: route.threadsDetail,
@@ -114,7 +118,7 @@ export const useChat = () => {
114118
router,
115119
selectedModel?.id,
116120
selectedProvider,
117-
currentAssistant,
121+
selectedAssistant,
118122
])
119123

120124
const restartModel = useCallback(
@@ -402,6 +406,7 @@ export const useChat = () => {
402406
accumulatedText,
403407
{
404408
tokenSpeed: useAppState.getState().tokenSpeed,
409+
assistant: currentAssistant,
405410
}
406411
)
407412

web-app/src/hooks/useMessages.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,18 @@ export const useMessages = create<MessageState>()((set, get) => ({
2828
}))
2929
},
3030
addMessage: (message) => {
31+
const assistants = useAssistant.getState().assistants
3132
const currentAssistant = useAssistant.getState().currentAssistant
33+
34+
const selectedAssistant =
35+
assistants.find((a) => a.id === currentAssistant.id) || assistants[0]
36+
3237
const newMessage = {
3338
...message,
3439
created_at: message.created_at || Date.now(),
3540
metadata: {
3641
...message.metadata,
37-
assistant: {
38-
id: currentAssistant?.id || '',
39-
name: currentAssistant?.name || '',
40-
avatar: currentAssistant?.avatar || '',
41-
instructions: currentAssistant?.instructions || '',
42-
parameters: currentAssistant?.parameters || '',
43-
},
42+
assistant: selectedAssistant,
4443
},
4544
}
4645
createMessage(newMessage).then((createdMessage) => {

web-app/src/providers/DataProvider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function DataProvider() {
2424
const { setMessages } = useMessages()
2525
const { checkForUpdate } = useAppUpdater()
2626
const { setServers } = useMCPServers()
27-
const { setAssistants } = useAssistant()
27+
const { setAssistants, initializeWithLastUsed } = useAssistant()
2828
const { setThreads } = useThreads()
2929
const navigate = useNavigate()
3030

@@ -37,6 +37,7 @@ export function DataProvider() {
3737
// Only update assistants if we have valid data
3838
if (data && Array.isArray(data) && data.length > 0) {
3939
setAssistants(data as unknown as Assistant[])
40+
initializeWithLastUsed()
4041
}
4142
})
4243
.catch((error) => {

0 commit comments

Comments
 (0)