Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web-app/src/constants/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export const localStorageKey = {
toolAvailability: 'tool-availability',
mcpGlobalPermissions: 'mcp-global-permissions',
lastUsedModel: 'last-used-model',
lastUsedAssistant: 'last-used-assistant',
}
67 changes: 61 additions & 6 deletions web-app/src/hooks/useAssistant.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
import { createAssistant, deleteAssistant } from '@/services/assistants'
import { Assistant as CoreAssistant } from '@janhq/core'
import { create } from 'zustand'
import { localStorageKey } from '@/constants/localStorage'

interface AssistantState {
assistants: Assistant[]
currentAssistant: Assistant
addAssistant: (assistant: Assistant) => void
updateAssistant: (assistant: Assistant) => void
deleteAssistant: (id: string) => void
setCurrentAssistant: (assistant: Assistant) => void
setCurrentAssistant: (assistant: Assistant, saveToStorage?: boolean) => void
setAssistants: (assistants: Assistant[]) => void
getLastUsedAssistant: () => string | null
setLastUsedAssistant: (assistantId: string) => void
initializeWithLastUsed: () => void
}

// Helper functions for localStorage
const getLastUsedAssistantId = (): string | null => {
try {
return localStorage.getItem(localStorageKey.lastUsedAssistant)
} catch (error) {
console.debug('Failed to get last used assistant from localStorage:', error)
return null
}
}

Check warning on line 27 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

21-27 lines are not covered with tests

const setLastUsedAssistantId = (assistantId: string) => {
try {
localStorage.setItem(localStorageKey.lastUsedAssistant, assistantId)
} catch (error) {
console.debug('Failed to set last used assistant in localStorage:', error)
}

Check warning on line 34 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

33-34 lines are not covered with tests
}

export const defaultAssistant: Assistant = {
Expand All @@ -19,9 +41,9 @@
parameters: {},
avatar: '👋',
description:
'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the users behalf.',
'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the user\'s behalf.',
instructions:
'You are a helpful AI assistant. Your primary goal is to assist users with their questions and tasks to the best of your abilities.\n\nWhen responding:\n- Answer directly from your knowledge when you can\n- Be concise, clear, and helpful\n- Admit when youre unsure rather than making things up\n\nIf tools are available to you:\n- Only use tools when they add real value to your response\n- Use tools when the user explicitly asks (e.g., "search for...", "calculate...", "run this code")\n- Use tools for information you dont know or that needs verification\n- Never use tools just because theyre available\n\nWhen using tools:\n- Use one tool at a time and wait for results\n- Use actual values as arguments, not variable names\n- Learn from each result before deciding next steps\n- Avoid repeating the same tool call with identical parameters\n\nRemember: Most questions can be answered without tools. Think first whether you need them.',
'You are a helpful AI assistant. Your primary goal is to assist users with their questions and tasks to the best of your abilities.\n\nWhen responding:\n- Answer directly from your knowledge when you can\n- Be concise, clear, and helpful\n- Admit when you\'re unsure rather than making things up\n\nIf tools are available to you:\n- Only use tools when they add real value to your response\n- Use tools when the user explicitly asks (e.g., "search for...", "calculate...", "run this code")\n- Use tools for information you don\'t know or that needs verification\n- Never use tools just because they\'re available\n\nWhen using tools:\n- Use one tool at a time and wait for results\n- Use actual values as arguments, not variable names\n- Learn from each result before deciding next steps\n- Avoid repeating the same tool call with identical parameters\n\nRemember: Most questions can be answered without tools. Think first whether you need them.',
}

export const useAssistant = create<AssistantState>()((set, get) => ({
Expand All @@ -30,7 +52,7 @@
addAssistant: (assistant) => {
set({ assistants: [...get().assistants, assistant] })
createAssistant(assistant as unknown as CoreAssistant).catch((error) => {
console.error('Failed to create assistant:', error)

Check warning on line 55 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

55 line is not covered with tests
})
},
updateAssistant: (assistant) => {
Expand All @@ -43,25 +65,58 @@
currentAssistant:
state.currentAssistant.id === assistant.id
? assistant
: state.currentAssistant,

Check warning on line 68 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

68 line is not covered with tests
})
// Create assistant already cover update logic
createAssistant(assistant as unknown as CoreAssistant).catch((error) => {
console.error('Failed to update assistant:', error)

Check warning on line 72 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

72 line is not covered with tests
})
},
deleteAssistant: (id) => {
const state = get()
deleteAssistant(
get().assistants.find((e) => e.id === id) as unknown as CoreAssistant
state.assistants.find((e) => e.id === id) as unknown as CoreAssistant
).catch((error) => {
console.error('Failed to delete assistant:', error)

Check warning on line 80 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

80 line is not covered with tests
})
set({ assistants: get().assistants.filter((a) => a.id !== id) })

// Check if we're deleting the current assistant
const wasCurrentAssistant = state.currentAssistant.id === id

set({ assistants: state.assistants.filter((a) => a.id !== id) })

// If the deleted assistant was current, fallback to default and update localStorage
if (wasCurrentAssistant) {
set({ currentAssistant: defaultAssistant })
setLastUsedAssistantId(defaultAssistant.id)
}

Check warning on line 92 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

90-92 lines are not covered with tests
},
setCurrentAssistant: (assistant) => {
setCurrentAssistant: (assistant, saveToStorage = true) => {
set({ currentAssistant: assistant })
if (saveToStorage) {
setLastUsedAssistantId(assistant.id)
}
},
setAssistants: (assistants) => {
set({ assistants })
},
getLastUsedAssistant: () => {
return getLastUsedAssistantId()
},

Check warning on line 105 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

104-105 lines are not covered with tests
setLastUsedAssistant: (assistantId) => {
setLastUsedAssistantId(assistantId)
},

Check warning on line 108 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

107-108 lines are not covered with tests
initializeWithLastUsed: () => {
const lastUsedId = getLastUsedAssistantId()
if (lastUsedId) {
const lastUsedAssistant = get().assistants.find(a => a.id === lastUsedId)
if (lastUsedAssistant) {
set({ currentAssistant: lastUsedAssistant })
} else {

Check warning on line 115 in web-app/src/hooks/useAssistant.ts

View workflow job for this annotation

GitHub Actions / coverage-check

110-115 lines are not covered with tests
// Fallback to default if last used assistant was deleted
set({ currentAssistant: defaultAssistant })
setLastUsedAssistantId(defaultAssistant.id)
}
}
},
}))
1 change: 1 addition & 0 deletions web-app/src/hooks/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@
accumulatedText,
{
tokenSpeed: useAppState.getState().tokenSpeed,
assistant: currentAssistant,
}
)

Expand Down Expand Up @@ -435,8 +436,8 @@
},
[
getCurrentThread,
resetTokenSpeed,

Check warning on line 439 in web-app/src/hooks/useChat.ts

View workflow job for this annotation

GitHub Actions / base_branch_cov

React Hook useCallback has a missing dependency: 'experimentalFeatures'. Either include it or remove the dependency array
currentProviderId,

Check warning on line 440 in web-app/src/hooks/useChat.ts

View workflow job for this annotation

GitHub Actions / test-on-macos

React Hook useCallback has a missing dependency: 'experimentalFeatures'. Either include it or remove the dependency array

Check warning on line 440 in web-app/src/hooks/useChat.ts

View workflow job for this annotation

GitHub Actions / test-on-windows-pr

React Hook useCallback has a missing dependency: 'experimentalFeatures'. Either include it or remove the dependency array

Check warning on line 440 in web-app/src/hooks/useChat.ts

View workflow job for this annotation

GitHub Actions / test-on-ubuntu

React Hook useCallback has a missing dependency: 'experimentalFeatures'. Either include it or remove the dependency array

Check warning on line 440 in web-app/src/hooks/useChat.ts

View workflow job for this annotation

GitHub Actions / coverage-check

React Hook useCallback has a missing dependency: 'experimentalFeatures'. Either include it or remove the dependency array
getProviderByName,
provider,
getMessages,
Expand Down
3 changes: 2 additions & 1 deletion web-app/src/providers/DataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function DataProvider() {
const { setMessages } = useMessages()
const { checkForUpdate } = useAppUpdater()
const { setServers } = useMCPServers()
const { setAssistants } = useAssistant()
const { setAssistants, initializeWithLastUsed } = useAssistant()
const { setThreads } = useThreads()
const navigate = useNavigate()

Expand All @@ -37,6 +37,7 @@ export function DataProvider() {
// Only update assistants if we have valid data
if (data && Array.isArray(data) && data.length > 0) {
setAssistants(data as unknown as Assistant[])
initializeWithLastUsed()
}
})
.catch((error) => {
Expand Down
Loading