mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 06:30:10 +08:00
feat: session settings (#10773)
* fix(home/Tabs): remove redundant isTopicView check in tab rendering * refactor(runtime): rename activeSessionId to activeSessionIdMap for clarity Update variable name to better reflect its purpose as a mapping structure * refactor(agent): add CreateAgentSessionResponse type and schema Add new type and schema for create session response to better reflect API contract * fix(useSessions): return null instead of undefined on session creation error Returning null provides better type safety and aligns with the response type Promise<CreateAgentSessionResponse | null> * refactor(useSessions): add return type to deleteSession callback * fix(useSessions): return session data or null from getSession Ensure getSession callback always returns a value (session data or null) to handle error cases properly and improve type safety * feat(hooks): add useActiveSession hook and handle null agentId in useSessions Add new hook to get active session from runtime and sessions data. Update useSessions to handle null agentId cases by returning early and adding null checks. * feat(hooks): add useActiveAgent hook to get active agent Expose active agent data by combining useRuntime and useAgent hooks * fix(agents): remove fake agent id handling and improve null checks - Replace fake agent id with null in HomePage - Remove fake id check in useAgent hook and throw error for null id - Simplify agent session initialization by removing fake id checks * refactor(hooks): replace useAgent with useActiveAgent for active agent state * feat(home): add session settings tab component Replace AgentSettingsTab with SessionSettingsTab to better handle session-specific settings. The new component includes essential and advanced settings sections with a more settings button. * refactor(settings): consolidate agent and session essential settings into single component Replace AgentEssentialSettings and SessionEssentialSettings with a unified EssentialSettings component that handles both agent and session types. This reduces code duplication and improves maintainability. * style(SelectAgentModelButton): improve model name display with truncation Add overflow-x-hidden to container and truncate to model name span to prevent text overflow * refactor(AgentSettings): replace Ellipsis with truncate for text overflow Use CSS truncate instead of Ellipsis component for better performance and consistency * refactor(chat-navbar): replace useAgent and useSession with useActiveAgent and useActiveSession Simplify component logic by using dedicated hooks for active agent and session * feat(ChatNavbar): add session settings button to breadcrumb Add clickable session label chip that opens session settings popup when active session exists * refactor(agents): improve session update hook and type definitions - Extract UpdateAgentBaseOptions type to shared types file - Update useUpdateSession to return both updateSession and updateModel functions - Modify components to use destructured updateSession from hook - Add null check for agentId in useUpdateSession - Add success toast option to session updates * refactor(components): rename agent prop to agentBase for clarity Update component name and prop to better reflect its purpose and improve code readability * refactor(ChatNavbar): rename SelectAgentModelButton to SelectAgentBaseModelButton and update usage Update component name to better reflect its purpose and adjust props to use activeSession instead of activeAgent for consistency * feat(i18n): add null id error message for agent retrieval Add error message for when agent ID is null across all supported languages * refactor(hooks): simplify agent and session hooks by returning destructured values Remove unnecessary intermediate variables and directly return hook results Update useSession to handle null agentId and sessionId cases * feat(i18n): add null session ID error message for all locales * refactor(home): rename SelectAgentModelButton to SelectAgentBaseModelButton The component was renamed to better reflect its purpose of selecting base models for agents. The functionality remains unchanged. * refactor(session): rename useUpdateAgent to useUpdateSession for clarity * refactor(home-tabs): replace useUpdateAgent with useUpdateSession hook Update session settings tab to use the new useUpdateSession hook which requires activeAgentId * style(AgentSettings): remove unnecessary gap class from ModelSetting * refactor(agents): improve error handling and remove duplicate code - Replace formatErrorMessageWithPrefix with getErrorMessage for better error handling - Move updateSession logic to useUpdateSession hook to avoid duplication * fix(ChatNavbar): prevent model update when activeAgent is missing Add activeAgent to dependency array and check its existence before updating model to avoid potential errors * feat(home/Tabs): add loading and error states for session settings Add Skeleton loader and Alert component to handle loading and error states when fetching session data in the settings tab * fix(home/Tabs): add h-full class to Skeleton for proper height * fix(AssistantsTab): remove weird effect hook for agent selection * refactor(chat-navbar): clean up unused code and update session handling remove commented out code in ChatNavbar.tsx and update ChatNavbarContent to use active agent/session hooks * style(home): remove negative margin from model name span * refactor(Agents): mark Agents component as deprecated * refactor: remove unused Agents and Assistants code --------- Co-authored-by: dev <verc20.dev@proton.me>
This commit is contained in:
parent
d1a9dfa3e6
commit
4eb3aa31ee
@ -9,6 +9,8 @@ import {
|
||||
CreateAgentRequest,
|
||||
CreateAgentResponse,
|
||||
CreateAgentResponseSchema,
|
||||
CreateAgentSessionResponse,
|
||||
CreateAgentSessionResponseSchema,
|
||||
CreateSessionForm,
|
||||
CreateSessionRequest,
|
||||
GetAgentResponse,
|
||||
@ -171,12 +173,12 @@ export class AgentApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
public async createSession(agentId: string, session: CreateSessionForm): Promise<GetAgentSessionResponse> {
|
||||
public async createSession(agentId: string, session: CreateSessionForm): Promise<CreateAgentSessionResponse> {
|
||||
const url = this.getSessionPaths(agentId).base
|
||||
try {
|
||||
const payload = session satisfies CreateSessionRequest
|
||||
const response = await this.axios.post(url, payload)
|
||||
const data = GetAgentSessionResponseSchema.parse(response.data)
|
||||
const data = CreateAgentSessionResponseSchema.parse(response.data)
|
||||
return data
|
||||
} catch (error) {
|
||||
throw processError(error, 'Failed to add session.')
|
||||
|
||||
@ -98,7 +98,7 @@ export const SessionModal: React.FC<Props> = ({
|
||||
const loadingRef = useRef(false)
|
||||
// const { setTimeoutTimer } = useTimer()
|
||||
const { createSession } = useSessions(agentId)
|
||||
const updateSession = useUpdateSession(agentId)
|
||||
const { updateSession } = useUpdateSession(agentId)
|
||||
const { agent } = useAgent(agentId)
|
||||
const isEditing = (session?: AgentSessionEntity) => session !== undefined
|
||||
|
||||
|
||||
4
src/renderer/src/hooks/agents/types.ts
Normal file
4
src/renderer/src/hooks/agents/types.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export type UpdateAgentBaseOptions = {
|
||||
/** Whether to show success toast after updating. Defaults to true. */
|
||||
showSuccessToast?: boolean
|
||||
}
|
||||
8
src/renderer/src/hooks/agents/useActiveAgent.ts
Normal file
8
src/renderer/src/hooks/agents/useActiveAgent.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { useRuntime } from '../useRuntime'
|
||||
import { useAgent } from './useAgent'
|
||||
|
||||
export const useActiveAgent = () => {
|
||||
const { chat } = useRuntime()
|
||||
const { activeAgentId } = chat
|
||||
return useAgent(activeAgentId)
|
||||
}
|
||||
9
src/renderer/src/hooks/agents/useActiveSession.ts
Normal file
9
src/renderer/src/hooks/agents/useActiveSession.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { useRuntime } from '../useRuntime'
|
||||
import { useSession } from './useSession'
|
||||
|
||||
export const useActiveSession = () => {
|
||||
const { chat } = useRuntime()
|
||||
const { activeSessionIdMap, activeAgentId } = chat
|
||||
const activeSessionId = activeAgentId ? activeSessionIdMap[activeAgentId] : null
|
||||
return useSession(activeAgentId, activeSessionId)
|
||||
}
|
||||
@ -11,8 +11,8 @@ export const useAgent = (id: string | null) => {
|
||||
const key = id ? client.agentPaths.withId(id) : null
|
||||
const { apiServerConfig, apiServerRunning } = useApiServer()
|
||||
const fetcher = useCallback(async () => {
|
||||
if (!id || id === 'fake') {
|
||||
return null
|
||||
if (!id) {
|
||||
throw new Error(t('agent.get.error.null_id'))
|
||||
}
|
||||
if (!apiServerConfig.enabled) {
|
||||
throw new Error(t('apiServer.messages.notEnabled'))
|
||||
|
||||
@ -17,7 +17,7 @@ export const useAgentSessionInitializer = () => {
|
||||
const dispatch = useAppDispatch()
|
||||
const client = useAgentClient()
|
||||
const { chat } = useRuntime()
|
||||
const { activeAgentId, activeSessionId } = chat
|
||||
const { activeAgentId, activeSessionIdMap } = chat
|
||||
|
||||
/**
|
||||
* Initialize session for the given agent by loading its sessions
|
||||
@ -25,11 +25,11 @@ export const useAgentSessionInitializer = () => {
|
||||
*/
|
||||
const initializeAgentSession = useCallback(
|
||||
async (agentId: string) => {
|
||||
if (!agentId || agentId === 'fake') return
|
||||
if (!agentId) return
|
||||
|
||||
try {
|
||||
// Check if this agent already has an active session
|
||||
const currentSessionId = activeSessionId[agentId]
|
||||
const currentSessionId = activeSessionIdMap[agentId]
|
||||
if (currentSessionId) {
|
||||
// Session already exists, just switch to session view
|
||||
dispatch(setActiveTopicOrSessionAction('session'))
|
||||
@ -58,21 +58,21 @@ export const useAgentSessionInitializer = () => {
|
||||
dispatch(setActiveTopicOrSessionAction('session'))
|
||||
}
|
||||
},
|
||||
[client, dispatch, activeSessionId]
|
||||
[client, dispatch, activeSessionIdMap]
|
||||
)
|
||||
|
||||
/**
|
||||
* Auto-initialize when activeAgentId changes
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (activeAgentId && activeAgentId !== 'fake') {
|
||||
if (activeAgentId) {
|
||||
// Check if we need to initialize this agent's session
|
||||
const hasActiveSession = activeSessionId[activeAgentId]
|
||||
const hasActiveSession = activeSessionIdMap[activeAgentId]
|
||||
if (!hasActiveSession) {
|
||||
initializeAgentSession(activeAgentId)
|
||||
}
|
||||
}
|
||||
}, [activeAgentId, activeSessionId, initializeAgentSession])
|
||||
}, [activeAgentId, activeSessionIdMap, initializeAgentSession])
|
||||
|
||||
return {
|
||||
initializeAgentSession
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { loadTopicMessagesThunk } from '@renderer/store/thunk/messageThunk'
|
||||
import { UpdateSessionForm } from '@renderer/types'
|
||||
import { buildAgentSessionTopicId } from '@renderer/utils/agentSession'
|
||||
import { useCallback, useEffect, useMemo } from 'react'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
|
||||
import { useAgentClient } from './useAgentClient'
|
||||
import { useUpdateSession } from './useUpdateSession'
|
||||
|
||||
export const useSession = (agentId: string, sessionId: string) => {
|
||||
export const useSession = (agentId: string | null, sessionId: string | null) => {
|
||||
const { t } = useTranslation()
|
||||
const client = useAgentClient()
|
||||
const key = client.getSessionPaths(agentId).withId(sessionId)
|
||||
const key = agentId && sessionId ? client.getSessionPaths(agentId).withId(sessionId) : null
|
||||
const dispatch = useAppDispatch()
|
||||
const sessionTopicId = useMemo(() => buildAgentSessionTopicId(sessionId), [sessionId])
|
||||
const sessionTopicId = useMemo(() => (sessionId ? buildAgentSessionTopicId(sessionId) : null), [sessionId])
|
||||
const { updateSession } = useUpdateSession(agentId)
|
||||
|
||||
const fetcher = async () => {
|
||||
if (!agentId) throw new Error(t('agent.get.error.null_id'))
|
||||
if (!sessionId) throw new Error(t('agent.session.get.error.null_id'))
|
||||
const data = await client.getSession(agentId, sessionId)
|
||||
return data
|
||||
}
|
||||
@ -24,26 +27,13 @@ export const useSession = (agentId: string, sessionId: string) => {
|
||||
// Use loadTopicMessagesThunk to load messages (with caching mechanism)
|
||||
// This ensures messages are preserved when switching between sessions/tabs
|
||||
useEffect(() => {
|
||||
if (sessionId) {
|
||||
if (sessionTopicId) {
|
||||
// loadTopicMessagesThunk will check if messages already exist in Redux
|
||||
// and skip loading if they do (unless forceReload is true)
|
||||
dispatch(loadTopicMessagesThunk(sessionTopicId))
|
||||
}
|
||||
}, [dispatch, sessionId, sessionTopicId])
|
||||
|
||||
const updateSession = useCallback(
|
||||
async (form: UpdateSessionForm) => {
|
||||
if (!agentId) return
|
||||
try {
|
||||
const result = await client.updateSession(agentId, form)
|
||||
mutate(result)
|
||||
} catch (error) {
|
||||
window.toast.error(t('agent.session.update.error.failed'))
|
||||
}
|
||||
},
|
||||
[agentId, client, mutate, t]
|
||||
)
|
||||
|
||||
return {
|
||||
session: data,
|
||||
error,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { CreateSessionForm } from '@renderer/types'
|
||||
import { CreateAgentSessionResponse, CreateSessionForm, GetAgentSessionResponse } from '@renderer/types'
|
||||
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -6,46 +6,50 @@ import useSWR from 'swr'
|
||||
|
||||
import { useAgentClient } from './useAgentClient'
|
||||
|
||||
export const useSessions = (agentId: string) => {
|
||||
export const useSessions = (agentId: string | null) => {
|
||||
const { t } = useTranslation()
|
||||
const client = useAgentClient()
|
||||
const key = client.getSessionPaths(agentId).base
|
||||
const key = agentId ? client.getSessionPaths(agentId).base : null
|
||||
|
||||
const fetcher = async () => {
|
||||
if (!agentId) throw new Error('No active agent.')
|
||||
const data = await client.listSessions(agentId)
|
||||
return data.data
|
||||
}
|
||||
const { data, error, isLoading, mutate } = useSWR(key, fetcher)
|
||||
|
||||
const createSession = useCallback(
|
||||
async (form: CreateSessionForm) => {
|
||||
async (form: CreateSessionForm): Promise<CreateAgentSessionResponse | null> => {
|
||||
if (!agentId) return null
|
||||
try {
|
||||
const result = await client.createSession(agentId, form)
|
||||
await mutate((prev) => [result, ...(prev ?? [])], { revalidate: false })
|
||||
return result
|
||||
} catch (error) {
|
||||
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.session.create.error.failed')))
|
||||
return undefined
|
||||
return null
|
||||
}
|
||||
},
|
||||
[agentId, client, mutate, t]
|
||||
)
|
||||
|
||||
// TODO: including messages field
|
||||
const getSession = useCallback(
|
||||
async (id: string) => {
|
||||
async (id: string): Promise<GetAgentSessionResponse | null> => {
|
||||
if (!agentId) return null
|
||||
try {
|
||||
const result = await client.getSession(agentId, id)
|
||||
mutate((prev) => prev?.map((session) => (session.id === result.id ? result : session)))
|
||||
return result
|
||||
} catch (error) {
|
||||
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.session.get.error.failed')))
|
||||
return null
|
||||
}
|
||||
},
|
||||
[agentId, client, mutate, t]
|
||||
)
|
||||
|
||||
const deleteSession = useCallback(
|
||||
async (id: string) => {
|
||||
async (id: string): Promise<boolean> => {
|
||||
if (!agentId) return false
|
||||
try {
|
||||
await client.deleteSession(agentId, id)
|
||||
|
||||
@ -4,20 +4,16 @@ import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { mutate } from 'swr'
|
||||
|
||||
import { UpdateAgentBaseOptions } from './types'
|
||||
import { useAgentClient } from './useAgentClient'
|
||||
|
||||
export type UpdateAgentOptions = {
|
||||
/** Whether to show success toast after updating. Defaults to true. */
|
||||
showSuccessToast?: boolean
|
||||
}
|
||||
|
||||
export const useUpdateAgent = () => {
|
||||
const { t } = useTranslation()
|
||||
const client = useAgentClient()
|
||||
const listKey = client.agentPaths.base
|
||||
|
||||
const updateAgent = useCallback(
|
||||
async (form: UpdateAgentForm, options?: UpdateAgentOptions) => {
|
||||
async (form: UpdateAgentForm, options?: UpdateAgentBaseOptions) => {
|
||||
try {
|
||||
const itemKey = client.agentPaths.withId(form.id)
|
||||
// may change to optimistic update
|
||||
@ -35,7 +31,7 @@ export const useUpdateAgent = () => {
|
||||
)
|
||||
|
||||
const updateModel = useCallback(
|
||||
async (agentId: string, modelId: string, options?: UpdateAgentOptions) => {
|
||||
async (agentId: string, modelId: string, options?: UpdateAgentBaseOptions) => {
|
||||
updateAgent({ id: agentId, model: modelId }, options)
|
||||
},
|
||||
[updateAgent]
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { ListAgentSessionsResponse, UpdateSessionForm } from '@renderer/types'
|
||||
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
||||
import { getErrorMessage } from '@renderer/utils/error'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { mutate } from 'swr'
|
||||
|
||||
import { UpdateAgentBaseOptions } from './types'
|
||||
import { useAgentClient } from './useAgentClient'
|
||||
|
||||
export const useUpdateSession = (agentId: string) => {
|
||||
export const useUpdateSession = (agentId: string | null) => {
|
||||
const { t } = useTranslation()
|
||||
const client = useAgentClient()
|
||||
const paths = client.getSessionPaths(agentId)
|
||||
const listKey = paths.base
|
||||
|
||||
const updateSession = useCallback(
|
||||
async (form: UpdateSessionForm) => {
|
||||
async (form: UpdateSessionForm, options?: UpdateAgentBaseOptions) => {
|
||||
if (!agentId) return
|
||||
const paths = client.getSessionPaths(agentId)
|
||||
const listKey = paths.base
|
||||
const sessionId = form.id
|
||||
try {
|
||||
const itemKey = paths.withId(sessionId)
|
||||
@ -24,13 +26,29 @@ export const useUpdateSession = (agentId: string) => {
|
||||
(prev) => prev?.map((session) => (session.id === result.id ? result : session)) ?? []
|
||||
)
|
||||
mutate(itemKey, result)
|
||||
window.toast.success(t('common.update_success'))
|
||||
if (options?.showSuccessToast ?? true) {
|
||||
window.toast.success(t('common.update_success'))
|
||||
}
|
||||
} catch (error) {
|
||||
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
|
||||
window.toast.error({ title: t('agent.session.update.error.failed'), description: getErrorMessage(error) })
|
||||
}
|
||||
},
|
||||
[agentId, client, listKey, paths, t]
|
||||
[agentId, client, t]
|
||||
)
|
||||
|
||||
return updateSession
|
||||
const updateModel = useCallback(
|
||||
async (sessionId: string, modelId: string, options?: UpdateAgentBaseOptions) => {
|
||||
if (!agentId) return
|
||||
return updateSession(
|
||||
{
|
||||
id: sessionId,
|
||||
model: modelId
|
||||
},
|
||||
options
|
||||
)
|
||||
},
|
||||
[agentId, updateSession]
|
||||
)
|
||||
|
||||
return { updateSession, updateModel }
|
||||
}
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Failed to get the agent."
|
||||
"failed": "Failed to get the agent.",
|
||||
"null_id": "Agent ID is null."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Failed to get the session"
|
||||
"failed": "Failed to get the session",
|
||||
"null_id": "Session ID is null"
|
||||
}
|
||||
},
|
||||
"label_one": "Session",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "获取智能体失败"
|
||||
"failed": "获取智能体失败",
|
||||
"null_id": "智能体 ID 为空。"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "获取会话失败"
|
||||
"failed": "获取会话失败",
|
||||
"null_id": "会话 ID 为空"
|
||||
}
|
||||
},
|
||||
"label_one": "会话",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "無法取得代理程式。"
|
||||
"failed": "無法取得代理程式。",
|
||||
"null_id": "代理程式 ID 為空。"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "無法取得工作階段"
|
||||
"failed": "無法取得工作階段",
|
||||
"null_id": "工作階段 ID 為空"
|
||||
}
|
||||
},
|
||||
"label_one": "會議",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Αποτυχία λήψης του πράκτορα."
|
||||
"failed": "Αποτυχία λήψης του πράκτορα.",
|
||||
"null_id": "Το ID του πράκτορα είναι null."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Αποτυχία λήψης της συνεδρίας"
|
||||
"failed": "Αποτυχία λήψης της συνεδρίας",
|
||||
"null_id": "Το ID της συνεδρίας είναι null"
|
||||
}
|
||||
},
|
||||
"label_one": "Συνεδρία",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "No se pudo obtener el agente."
|
||||
"failed": "No se pudo obtener el agente.",
|
||||
"null_id": "El ID del agente es nulo."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Error al obtener la sesión"
|
||||
"failed": "Error al obtener la sesión",
|
||||
"null_id": "El ID de sesión es nulo"
|
||||
}
|
||||
},
|
||||
"label_one": "Sesión",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Échec de l'obtention de l'agent."
|
||||
"failed": "Échec de l'obtention de l'agent.",
|
||||
"null_id": "L'ID de l'agent est nul."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Échec de l'obtention de la session"
|
||||
"failed": "Échec de l'obtention de la session",
|
||||
"null_id": "L'ID de session est nul"
|
||||
}
|
||||
},
|
||||
"label_one": "Session",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "エージェントの取得に失敗しました。"
|
||||
"failed": "エージェントの取得に失敗しました。",
|
||||
"null_id": "エージェント ID が null です。"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "セッションの取得に失敗しました"
|
||||
"failed": "セッションの取得に失敗しました",
|
||||
"null_id": "セッション ID が null です"
|
||||
}
|
||||
},
|
||||
"label_one": "セッション",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Falha ao obter o agente."
|
||||
"failed": "Falha ao obter o agente.",
|
||||
"null_id": "O ID do agente é nulo."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Falha ao obter a sessão"
|
||||
"failed": "Falha ao obter a sessão",
|
||||
"null_id": "O ID da sessão é nulo"
|
||||
}
|
||||
},
|
||||
"label_one": "Sessão",
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Не удалось получить агента."
|
||||
"failed": "Не удалось получить агента.",
|
||||
"null_id": "ID агента равен null."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@ -73,7 +74,8 @@
|
||||
},
|
||||
"get": {
|
||||
"error": {
|
||||
"failed": "Не удалось получить сеанс"
|
||||
"failed": "Не удалось получить сеанс",
|
||||
"null_id": "ID сессии равен null"
|
||||
}
|
||||
},
|
||||
"label_one": "Сессия",
|
||||
|
||||
@ -49,7 +49,8 @@ const Chat: FC<Props> = (props) => {
|
||||
const { isTopNavbar } = useNavbarPosition()
|
||||
const chatMaxWidth = useChatMaxWidth()
|
||||
const { chat } = useRuntime()
|
||||
const { activeTopicOrSession, activeAgentId, activeSessionId } = chat
|
||||
const { activeTopicOrSession, activeAgentId, activeSessionIdMap } = chat
|
||||
const activeSessionId = activeAgentId ? activeSessionIdMap[activeAgentId] : null
|
||||
const { apiServer } = useSettings()
|
||||
|
||||
const mainRef = React.useRef<HTMLDivElement>(null)
|
||||
@ -147,8 +148,7 @@ const Chat: FC<Props> = (props) => {
|
||||
if (activeAgentId === null) {
|
||||
return () => <div> Active Agent ID is invalid.</div>
|
||||
}
|
||||
const sessionId = activeSessionId[activeAgentId]
|
||||
if (!sessionId) {
|
||||
if (!activeSessionId) {
|
||||
return () => <div> Active Session ID is invalid.</div>
|
||||
}
|
||||
if (!apiServer.enabled) {
|
||||
@ -158,18 +158,17 @@ const Chat: FC<Props> = (props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return () => <AgentSessionMessages agentId={activeAgentId} sessionId={sessionId} />
|
||||
return () => <AgentSessionMessages agentId={activeAgentId} sessionId={activeSessionId} />
|
||||
}, [activeAgentId, activeSessionId, apiServer.enabled, t])
|
||||
|
||||
const SessionInputBar = useMemo(() => {
|
||||
if (activeAgentId === null) {
|
||||
return () => <div> Active Agent ID is invalid.</div>
|
||||
}
|
||||
const sessionId = activeSessionId[activeAgentId]
|
||||
if (!sessionId) {
|
||||
if (!activeSessionId) {
|
||||
return () => <div> Active Session ID is invalid.</div>
|
||||
}
|
||||
return () => <AgentSessionInputbar agentId={activeAgentId} sessionId={sessionId} />
|
||||
return () => <AgentSessionInputbar agentId={activeAgentId} sessionId={activeSessionId} />
|
||||
}, [activeAgentId, activeSessionId])
|
||||
|
||||
// TODO: more info
|
||||
@ -235,10 +234,8 @@ const Chat: FC<Props> = (props) => {
|
||||
</>
|
||||
)}
|
||||
{activeTopicOrSession === 'session' && !activeAgentId && <AgentInvalid />}
|
||||
{activeTopicOrSession === 'session' && activeAgentId && !activeSessionId[activeAgentId] && (
|
||||
<SessionInvalid />
|
||||
)}
|
||||
{activeTopicOrSession === 'session' && activeAgentId && activeSessionId[activeAgentId] && (
|
||||
{activeTopicOrSession === 'session' && activeAgentId && !activeSessionId && <SessionInvalid />}
|
||||
{activeTopicOrSession === 'session' && activeAgentId && activeSessionId && (
|
||||
<>
|
||||
<SessionMessages />
|
||||
<SessionInputBar />
|
||||
|
||||
@ -64,6 +64,14 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
||||
})
|
||||
}
|
||||
|
||||
// const handleUpdateModel = useCallback(
|
||||
// async (model: ApiModel) => {
|
||||
// if (!activeSession || !activeAgent) return
|
||||
// return updateModel(activeSession.id, model.id, { showSuccessToast: false })
|
||||
// },
|
||||
// [activeAgent, activeSession, updateModel]
|
||||
// )
|
||||
|
||||
return (
|
||||
<NavbarHeader className="home-navbar">
|
||||
<div className="flex min-w-0 flex-1 shrink items-center overflow-auto">
|
||||
|
||||
@ -117,7 +117,7 @@ const HomePage: FC = () => {
|
||||
type: 'chat'
|
||||
})
|
||||
} else if (activeTopicOrSession === 'topic') {
|
||||
dispatch(setActiveAgentId('fake'))
|
||||
dispatch(setActiveAgentId(null))
|
||||
}
|
||||
}, [activeTopicOrSession, dispatch, setActiveAssistant])
|
||||
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { Button, Divider } from '@heroui/react'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { AgentSettingsPopup } from '@renderer/pages/settings/AgentSettings'
|
||||
import AdvancedSettings from '@renderer/pages/settings/AgentSettings/AdvancedSettings'
|
||||
import AgentEssentialSettings from '@renderer/pages/settings/AgentSettings/AgentEssentialSettings'
|
||||
import { GetAgentResponse } from '@renderer/types/agent'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface Props {
|
||||
agent: GetAgentResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateAgent>['updateAgent']
|
||||
}
|
||||
|
||||
const AgentSettingsTab: FC<Props> = ({ agent, update }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const onMoreSetting = () => {
|
||||
if (agent?.id) {
|
||||
AgentSettingsPopup.show({ agentId: agent.id! })
|
||||
}
|
||||
}
|
||||
|
||||
if (!agent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
|
||||
<AgentEssentialSettings agent={agent} update={update} showModelSetting={false} />
|
||||
<AdvancedSettings agentBase={agent} update={update} />
|
||||
<Divider className="my-2" />
|
||||
<Button size="sm" fullWidth onPress={onMoreSetting}>
|
||||
{t('settings.moresetting.label')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentSettingsTab
|
||||
@ -11,7 +11,7 @@ import { useAppDispatch } from '@renderer/store'
|
||||
import { addIknowAction } from '@renderer/store/runtime'
|
||||
import { Assistant, AssistantsSortType } from '@renderer/types'
|
||||
import { getErrorMessage } from '@renderer/utils'
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FC, useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -80,12 +80,6 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
||||
updateAssistants
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!agentsLoading && agents.length > 0 && !activeAgentId && apiServerConfig.enabled) {
|
||||
setActiveAgentId(agents[0].id)
|
||||
}
|
||||
}, [agentsLoading, agents, activeAgentId, setActiveAgentId, apiServerConfig.enabled])
|
||||
|
||||
const onDeleteAssistant = useCallback(
|
||||
(assistant: Assistant) => {
|
||||
const remaining = assistants.filter((a) => a.id !== assistant.id)
|
||||
|
||||
43
src/renderer/src/pages/home/Tabs/SessionSettingsTab.tsx
Normal file
43
src/renderer/src/pages/home/Tabs/SessionSettingsTab.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { Button, Divider } from '@heroui/react'
|
||||
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
|
||||
import { SessionSettingsPopup } from '@renderer/pages/settings/AgentSettings'
|
||||
import AdvancedSettings from '@renderer/pages/settings/AgentSettings/AdvancedSettings'
|
||||
import EssentialSettings from '@renderer/pages/settings/AgentSettings/EssentialSettings'
|
||||
import { GetAgentSessionResponse } from '@renderer/types'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface Props {
|
||||
session: GetAgentSessionResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateSession>['updateSession']
|
||||
}
|
||||
|
||||
const SessionSettingsTab: FC<Props> = ({ session, update }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const onMoreSetting = () => {
|
||||
if (session?.id) {
|
||||
SessionSettingsPopup.show({
|
||||
agentId: session.agent_id,
|
||||
sessionId: session.id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
|
||||
<EssentialSettings agentBase={session} update={update} />
|
||||
<AdvancedSettings agentBase={session} update={update} />
|
||||
<Divider className="my-2" />
|
||||
<Button size="sm" fullWidth onPress={onMoreSetting}>
|
||||
{t('settings.moresetting.label')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SessionSettingsTab
|
||||
@ -1,71 +0,0 @@
|
||||
import { Alert, Button, Spinner } from '@heroui/react'
|
||||
import { AgentModal } from '@renderer/components/Popups/agent/AgentModal'
|
||||
import { useAgents } from '@renderer/hooks/agents/useAgents'
|
||||
import { useAgentSessionInitializer } from '@renderer/hooks/agents/useAgentSessionInitializer'
|
||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { setActiveAgentId as setActiveAgentIdAction } from '@renderer/store/runtime'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { FC, useCallback, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AgentItem from './AgentItem'
|
||||
|
||||
interface AssistantsTabProps {}
|
||||
|
||||
export const Agents: FC<AssistantsTabProps> = () => {
|
||||
const { agents, deleteAgent, isLoading, error } = useAgents()
|
||||
const { t } = useTranslation()
|
||||
const { chat } = useRuntime()
|
||||
const { activeAgentId } = chat
|
||||
const { initializeAgentSession } = useAgentSessionInitializer()
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const setActiveAgentId = useCallback(
|
||||
async (id: string) => {
|
||||
dispatch(setActiveAgentIdAction(id))
|
||||
// Initialize the session for this agent
|
||||
await initializeAgentSession(id)
|
||||
},
|
||||
[dispatch, initializeAgentSession]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && agents.length > 0 && !activeAgentId) {
|
||||
setActiveAgentId(agents[0].id)
|
||||
}
|
||||
}, [isLoading, agents, activeAgentId, setActiveAgentId])
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <Spinner />}
|
||||
{error && <Alert color="danger" title={t('agent.list.error.failed')} />}
|
||||
{!isLoading &&
|
||||
!error &&
|
||||
agents.map((agent) => (
|
||||
<AgentItem
|
||||
key={agent.id}
|
||||
agent={agent}
|
||||
isActive={agent.id === activeAgentId}
|
||||
onDelete={() => deleteAgent(agent.id)}
|
||||
onPress={() => {
|
||||
setActiveAgentId(agent.id)
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<AgentModal
|
||||
trigger={{
|
||||
content: (
|
||||
<Button
|
||||
onPress={(e) => e.continuePropagation()}
|
||||
startContent={<Plus size={16} className="mr-1 shrink-0 translate-x-[-2px]" />}
|
||||
className="w-full justify-start bg-transparent text-foreground-500 hover:bg-[var(--color-list-item)]">
|
||||
{t('agent.add.title')}
|
||||
</Button>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,208 +0,0 @@
|
||||
import { DownOutlined, RightOutlined } from '@ant-design/icons'
|
||||
import { Button } from '@heroui/react'
|
||||
import { DraggableList } from '@renderer/components/DraggableList'
|
||||
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
|
||||
import { useAssistantsTabSortType } from '@renderer/hooks/useStore'
|
||||
import { useTags } from '@renderer/hooks/useTags'
|
||||
import { Assistant, AssistantsSortType } from '@renderer/types'
|
||||
import { Tooltip } from 'antd'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { FC, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AssistantItem from './AssistantItem'
|
||||
import { SectionName } from './SectionName'
|
||||
|
||||
interface AssistantsProps {
|
||||
activeAssistant: Assistant
|
||||
setActiveAssistant: (assistant: Assistant) => void
|
||||
onCreateAssistant: () => void
|
||||
onCreateDefaultAssistant: () => void
|
||||
}
|
||||
|
||||
const Assistants: FC<AssistantsProps> = ({
|
||||
activeAssistant,
|
||||
setActiveAssistant,
|
||||
onCreateAssistant,
|
||||
onCreateDefaultAssistant
|
||||
}) => {
|
||||
const { assistants, removeAssistant, copyAssistant, updateAssistants } = useAssistants()
|
||||
const [dragging, setDragging] = useState(false)
|
||||
const { addAssistantPreset } = useAssistantPresets()
|
||||
const { t } = useTranslation()
|
||||
const { getGroupedAssistants, collapsedTags, toggleTagCollapse } = useTags()
|
||||
const { assistantsTabSortType = 'list', setAssistantsTabSortType } = useAssistantsTabSortType()
|
||||
|
||||
const onDelete = useCallback(
|
||||
(assistant: Assistant) => {
|
||||
const remaining = assistants.filter((a) => a.id !== assistant.id)
|
||||
if (assistant.id === activeAssistant?.id) {
|
||||
const newActive = remaining[remaining.length - 1]
|
||||
newActive ? setActiveAssistant(newActive) : onCreateDefaultAssistant()
|
||||
}
|
||||
removeAssistant(assistant.id)
|
||||
},
|
||||
[activeAssistant, assistants, removeAssistant, setActiveAssistant, onCreateDefaultAssistant]
|
||||
)
|
||||
|
||||
const handleSortByChange = useCallback(
|
||||
(sortType: AssistantsSortType) => {
|
||||
setAssistantsTabSortType(sortType)
|
||||
},
|
||||
[setAssistantsTabSortType]
|
||||
)
|
||||
|
||||
const handleGroupReorder = useCallback(
|
||||
(tag: string, newGroupList: Assistant[]) => {
|
||||
let insertIndex = 0
|
||||
const newGlobal = assistants.map((a) => {
|
||||
const tags = a.tags?.length ? a.tags : [t('assistants.tags.untagged')]
|
||||
if (tags.includes(tag)) {
|
||||
const replaced = newGroupList[insertIndex]
|
||||
insertIndex += 1
|
||||
return replaced
|
||||
}
|
||||
return a
|
||||
})
|
||||
updateAssistants(newGlobal)
|
||||
},
|
||||
[assistants, t, updateAssistants]
|
||||
)
|
||||
|
||||
const renderAddAssistantButton = useMemo(() => {
|
||||
return (
|
||||
<Button
|
||||
onPress={onCreateAssistant}
|
||||
className="w-full justify-start bg-transparent text-foreground-500 hover:bg-[var(--color-list-item)]">
|
||||
<Plus size={16} style={{ marginRight: 4, flexShrink: 0 }} />
|
||||
{t('chat.add.assistant.title')}
|
||||
</Button>
|
||||
)
|
||||
}, [onCreateAssistant, t])
|
||||
|
||||
if (assistantsTabSortType === 'tags') {
|
||||
return (
|
||||
<>
|
||||
<SectionName name={t('common.assistant_other')} />
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
{getGroupedAssistants.map((group) => (
|
||||
<TagsContainer key={group.tag}>
|
||||
{group.tag !== t('assistants.tags.untagged') && (
|
||||
<GroupTitle onClick={() => toggleTagCollapse(group.tag)}>
|
||||
<Tooltip title={group.tag}>
|
||||
<GroupTitleName>
|
||||
{collapsedTags[group.tag] ? (
|
||||
<RightOutlined style={{ fontSize: '10px', marginRight: '5px' }} />
|
||||
) : (
|
||||
<DownOutlined style={{ fontSize: '10px', marginRight: '5px' }} />
|
||||
)}
|
||||
{group.tag}
|
||||
</GroupTitleName>
|
||||
</Tooltip>
|
||||
<GroupTitleDivider />
|
||||
</GroupTitle>
|
||||
)}
|
||||
{!collapsedTags[group.tag] && (
|
||||
<div>
|
||||
<DraggableList
|
||||
list={group.assistants}
|
||||
onUpdate={(newList) => handleGroupReorder(group.tag, newList)}
|
||||
onDragStart={() => setDragging(true)}
|
||||
onDragEnd={() => setDragging(false)}>
|
||||
{(assistant) => (
|
||||
<AssistantItem
|
||||
key={assistant.id}
|
||||
assistant={assistant}
|
||||
isActive={assistant.id === activeAssistant.id}
|
||||
sortBy={assistantsTabSortType}
|
||||
onSwitch={setActiveAssistant}
|
||||
onDelete={onDelete}
|
||||
addPreset={addAssistantPreset}
|
||||
copyAssistant={copyAssistant}
|
||||
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||
handleSortByChange={handleSortByChange}
|
||||
/>
|
||||
)}
|
||||
</DraggableList>
|
||||
</div>
|
||||
)}
|
||||
</TagsContainer>
|
||||
))}
|
||||
{renderAddAssistantButton}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SectionName name={t('common.assistant_other')} />
|
||||
<DraggableList
|
||||
list={assistants}
|
||||
onUpdate={updateAssistants}
|
||||
onDragStart={() => setDragging(true)}
|
||||
onDragEnd={() => setDragging(false)}>
|
||||
{(assistant) => (
|
||||
<AssistantItem
|
||||
key={assistant.id}
|
||||
assistant={assistant}
|
||||
isActive={assistant.id === activeAssistant.id}
|
||||
sortBy={assistantsTabSortType}
|
||||
onSwitch={setActiveAssistant}
|
||||
onDelete={onDelete}
|
||||
addPreset={addAssistantPreset}
|
||||
copyAssistant={copyAssistant}
|
||||
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||
handleSortByChange={handleSortByChange}
|
||||
/>
|
||||
)}
|
||||
</DraggableList>
|
||||
{!dragging && renderAddAssistantButton}
|
||||
<div style={{ minHeight: 10 }}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 样式组件
|
||||
|
||||
const TagsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const GroupTitle = styled.div`
|
||||
color: var(--color-text-2);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
margin: 5px 0;
|
||||
`
|
||||
|
||||
const GroupTitleName = styled.div`
|
||||
max-width: 50%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 0 4px;
|
||||
color: var(--color-text);
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const GroupTitleDivider = styled.div`
|
||||
flex: 1;
|
||||
border-top: 1px solid var(--color-border);
|
||||
`
|
||||
|
||||
export default Assistants
|
||||
@ -33,8 +33,8 @@ interface SessionItemProps {
|
||||
const SessionItem: FC<SessionItemProps> = ({ session, agentId, isDisabled, isLoading, onDelete, onPress }) => {
|
||||
const { t } = useTranslation()
|
||||
const { chat } = useRuntime()
|
||||
const updateSession = useUpdateSession(agentId)
|
||||
const activeSessionId = chat.activeSessionId[agentId]
|
||||
const { updateSession } = useUpdateSession(agentId)
|
||||
const activeSessionId = chat.activeSessionIdMap[agentId]
|
||||
const [isConfirmingDeletion, setIsConfirmingDeletion] = useState(false)
|
||||
const { setTimeoutTimer } = useTimer()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@ -30,7 +30,7 @@ const Sessions: React.FC<SessionsProps> = ({ agentId }) => {
|
||||
const { agent } = useAgent(agentId)
|
||||
const { sessions, isLoading, error, deleteSession, createSession } = useSessions(agentId)
|
||||
const { chat } = useRuntime()
|
||||
const { activeSessionId, sessionWaiting } = chat
|
||||
const { activeSessionIdMap, sessionWaiting } = chat
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const setActiveSessionId = useCallback(
|
||||
@ -75,24 +75,24 @@ const Sessions: React.FC<SessionsProps> = ({ agentId }) => {
|
||||
[agentId, deleteSession, dispatch, sessions, t]
|
||||
)
|
||||
|
||||
const currentActiveSessionId = activeSessionId[agentId]
|
||||
const activeSessionId = activeSessionIdMap[agentId]
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && sessions.length > 0 && !currentActiveSessionId) {
|
||||
if (!isLoading && sessions.length > 0 && !activeSessionId) {
|
||||
setActiveSessionId(agentId, sessions[0].id)
|
||||
}
|
||||
}, [isLoading, sessions, currentActiveSessionId, agentId, setActiveSessionId])
|
||||
}, [isLoading, sessions, activeSessionId, agentId, setActiveSessionId])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentActiveSessionId) {
|
||||
if (activeSessionId) {
|
||||
dispatch(
|
||||
newMessagesActions.setTopicFulfilled({
|
||||
topicId: buildAgentSessionTopicId(currentActiveSessionId),
|
||||
topicId: buildAgentSessionTopicId(activeSessionId),
|
||||
fulfilled: false
|
||||
})
|
||||
)
|
||||
}
|
||||
}, [currentActiveSessionId, dispatch])
|
||||
}, [activeSessionId, dispatch])
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Alert, Skeleton } from '@heroui/react'
|
||||
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
||||
import { useAgent } from '@renderer/hooks/agents/useAgent'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { useActiveSession } from '@renderer/hooks/agents/useActiveSession'
|
||||
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
|
||||
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
||||
@ -8,13 +9,13 @@ import { useShowTopics } from '@renderer/hooks/useStore'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import { Assistant, Topic } from '@renderer/types'
|
||||
import { Tab } from '@renderer/types/chat'
|
||||
import { classNames, uuid } from '@renderer/utils'
|
||||
import { classNames, getErrorMessage, uuid } from '@renderer/utils'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AgentSettingsTab from './AgentSettingsTab'
|
||||
import Assistants from './AssistantsTab'
|
||||
import SessionSettingsTab from './SessionSettingsTab'
|
||||
import Settings from './SettingsTab'
|
||||
import Topics from './TopicsTab'
|
||||
|
||||
@ -47,8 +48,8 @@ const HomeTabs: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
const { chat } = useRuntime()
|
||||
const { activeTopicOrSession, activeAgentId } = chat
|
||||
const { agent } = useAgent(activeAgentId)
|
||||
const { updateAgent } = useUpdateAgent()
|
||||
const { session, isLoading: isSessionLoading, error: sessionError } = useActiveSession()
|
||||
const { updateSession } = useUpdateSession(activeAgentId)
|
||||
|
||||
const isSessionView = activeTopicOrSession === 'session'
|
||||
const isTopicView = activeTopicOrSession === 'topic'
|
||||
@ -125,7 +126,7 @@ const HomeTabs: FC<Props> = ({
|
||||
</CustomTabs>
|
||||
)}
|
||||
|
||||
{position === 'right' && topicPosition === 'right' && isTopicView && (
|
||||
{position === 'right' && topicPosition === 'right' && (
|
||||
<CustomTabs>
|
||||
<TabItem active={tab === 'topic'} onClick={() => setTab('topic')}>
|
||||
{t('common.topics')}
|
||||
@ -154,7 +155,20 @@ const HomeTabs: FC<Props> = ({
|
||||
/>
|
||||
)}
|
||||
{tab === 'settings' && isTopicView && <Settings assistant={activeAssistant} />}
|
||||
{tab === 'settings' && isSessionView && <AgentSettingsTab agent={agent} update={updateAgent} />}
|
||||
{tab === 'settings' && isSessionView && !sessionError && (
|
||||
<Skeleton isLoaded={!isSessionLoading} className="h-full">
|
||||
<SessionSettingsTab session={session} update={updateSession} />
|
||||
</Skeleton>
|
||||
)}
|
||||
{tab === 'settings' && isSessionView && sessionError && (
|
||||
<div className="w-[var(--assistants-width)] p-2 px-3 pt-4">
|
||||
<Alert
|
||||
color="danger"
|
||||
title={t('agent.session.get.error.failed')}
|
||||
description={getErrorMessage(sessionError)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</TabContent>
|
||||
</Container>
|
||||
)
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { BreadcrumbItem, Breadcrumbs, Chip, cn } from '@heroui/react'
|
||||
import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer'
|
||||
import { permissionModeCards } from '@renderer/constants/permissionModes'
|
||||
import { useAgent } from '@renderer/hooks/agents/useAgent'
|
||||
import { useSession } from '@renderer/hooks/agents/useSession'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { useActiveAgent } from '@renderer/hooks/agents/useActiveAgent'
|
||||
import { useActiveSession } from '@renderer/hooks/agents/useActiveSession'
|
||||
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
|
||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||
import { ApiModel, Assistant, PermissionMode } from '@renderer/types'
|
||||
import { AgentEntity, AgentSessionEntity, ApiModel, Assistant, PermissionMode } from '@renderer/types'
|
||||
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
||||
import { t } from 'i18next'
|
||||
import { FC, ReactNode, useCallback } from 'react'
|
||||
|
||||
import { AgentSettingsPopup } from '../../settings/AgentSettings'
|
||||
import { AgentLabel } from '../../settings/AgentSettings/shared'
|
||||
import SelectAgentModelButton from './SelectAgentModelButton'
|
||||
import { AgentSettingsPopup, SessionSettingsPopup } from '../../settings/AgentSettings'
|
||||
import { AgentLabel, SessionLabel } from '../../settings/AgentSettings/shared'
|
||||
import SelectAgentBaseModelButton from './SelectAgentBaseModelButton'
|
||||
import SelectModelButton from './SelectModelButton'
|
||||
|
||||
interface Props {
|
||||
@ -21,41 +21,67 @@ interface Props {
|
||||
|
||||
const ChatNavbarContent: FC<Props> = ({ assistant }) => {
|
||||
const { chat } = useRuntime()
|
||||
const { activeTopicOrSession, activeAgentId } = chat
|
||||
const sessionId = activeAgentId ? (chat.activeSessionId[activeAgentId] ?? null) : null
|
||||
const { agent } = useAgent(activeAgentId)
|
||||
const { updateModel } = useUpdateAgent()
|
||||
const { activeTopicOrSession } = chat
|
||||
const { agent: activeAgent } = useActiveAgent()
|
||||
const { session: activeSession } = useActiveSession()
|
||||
const { updateModel } = useUpdateSession(activeAgent?.id ?? null)
|
||||
|
||||
const handleUpdateModel = useCallback(
|
||||
async (model: ApiModel) => {
|
||||
if (!agent) return
|
||||
return updateModel(agent.id, model.id, { showSuccessToast: false })
|
||||
if (!activeAgent || !activeSession) return
|
||||
return updateModel(activeSession.id, model.id, { showSuccessToast: false })
|
||||
},
|
||||
[agent, updateModel]
|
||||
[activeAgent, activeSession, updateModel]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{activeTopicOrSession === 'topic' && <SelectModelButton assistant={assistant} />}
|
||||
{activeTopicOrSession === 'session' && agent && (
|
||||
{activeTopicOrSession === 'session' && activeAgent && (
|
||||
<HorizontalScrollContainer>
|
||||
<Breadcrumbs classNames={{ base: 'flex', list: 'flex-nowrap' }}>
|
||||
<Breadcrumbs
|
||||
classNames={{
|
||||
base: 'flex',
|
||||
list: 'flex-nowrap'
|
||||
}}>
|
||||
<BreadcrumbItem
|
||||
onPress={() => AgentSettingsPopup.show({ agentId: agent.id })}
|
||||
classNames={{ base: 'self-stretch', item: 'h-full' }}>
|
||||
onPress={() => AgentSettingsPopup.show({ agentId: activeAgent.id })}
|
||||
classNames={{
|
||||
base: 'self-stretch',
|
||||
item: 'h-full'
|
||||
}}>
|
||||
<Chip size="md" variant="light" className="h-full transition-background hover:bg-foreground-100">
|
||||
<AgentLabel
|
||||
agent={agent}
|
||||
classNames={{ name: 'max-w-50 font-bold text-xs', avatar: 'h-2 w-2 ml-[-4px]', container: 'gap-1.5' }}
|
||||
agent={activeAgent}
|
||||
classNames={{ name: 'max-w-40 font-bold text-xs', avatar: 'h-4.5 w-4.5', container: 'gap-1.5' }}
|
||||
/>
|
||||
</Chip>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<SelectAgentModelButton agent={agent} onSelect={handleUpdateModel} />
|
||||
</BreadcrumbItem>
|
||||
{activeAgentId && sessionId && (
|
||||
{activeSession && (
|
||||
<BreadcrumbItem
|
||||
onPress={() =>
|
||||
SessionSettingsPopup.show({
|
||||
agentId: activeAgent.id,
|
||||
sessionId: activeSession.id
|
||||
})
|
||||
}
|
||||
classNames={{
|
||||
base: 'self-stretch',
|
||||
item: 'h-full'
|
||||
}}>
|
||||
<Chip size="md" variant="light" className="h-full transition-background hover:bg-foreground-100">
|
||||
<SessionLabel session={activeSession} className="max-w-40 font-bold text-xs" />
|
||||
</Chip>
|
||||
</BreadcrumbItem>
|
||||
)}
|
||||
{activeSession && (
|
||||
<BreadcrumbItem>
|
||||
<SessionWorkspaceMeta agentId={activeAgentId} sessionId={sessionId} />
|
||||
<SelectAgentBaseModelButton agentBase={activeSession} onSelect={handleUpdateModel} />
|
||||
</BreadcrumbItem>
|
||||
)}
|
||||
{activeAgent && activeSession && (
|
||||
<BreadcrumbItem>
|
||||
<SessionWorkspaceMeta agent={activeAgent} session={activeSession} />
|
||||
</BreadcrumbItem>
|
||||
)}
|
||||
</Breadcrumbs>
|
||||
@ -65,9 +91,7 @@ const ChatNavbarContent: FC<Props> = ({ assistant }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const SessionWorkspaceMeta: FC<{ agentId: string; sessionId: string }> = ({ agentId, sessionId }) => {
|
||||
const { agent } = useAgent(agentId)
|
||||
const { session } = useSession(agentId, sessionId)
|
||||
const SessionWorkspaceMeta: FC<{ agent: AgentEntity; session: AgentSessionEntity }> = ({ agent, session }) => {
|
||||
if (!session || !agent) {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -12,12 +12,12 @@ import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface Props {
|
||||
agent: AgentBaseWithId
|
||||
agentBase: AgentBaseWithId
|
||||
onSelect: (model: ApiModel) => Promise<void>
|
||||
isDisabled?: boolean
|
||||
}
|
||||
|
||||
const SelectAgentModelButton: FC<Props> = ({ agent, onSelect, isDisabled }) => {
|
||||
const SelectAgentBaseModelButton: FC<Props> = ({ agentBase: agent, onSelect, isDisabled }) => {
|
||||
const { t } = useTranslation()
|
||||
const model = useApiModel({ id: agent?.model })
|
||||
|
||||
@ -42,9 +42,9 @@ const SelectAgentModelButton: FC<Props> = ({ agent, onSelect, isDisabled }) => {
|
||||
className="nodrag rounded-2xl px-1 py-3"
|
||||
onPress={onSelectModel}
|
||||
isDisabled={isDisabled}>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="flex items-center gap-1.5 overflow-x-hidden">
|
||||
<ModelAvatar model={model ? apiModelAdapter(model) : undefined} size={20} />
|
||||
<span className="-mr-0.5 font-medium">
|
||||
<span className="truncate font-medium">
|
||||
{model ? model.name : t('button.select_model')} {providerName ? ' | ' + providerName : ''}
|
||||
</span>
|
||||
</div>
|
||||
@ -53,4 +53,4 @@ const SelectAgentModelButton: FC<Props> = ({ agent, onSelect, isDisabled }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectAgentModelButton
|
||||
export default SelectAgentBaseModelButton
|
||||
@ -23,7 +23,7 @@ type AdvancedSettingsProps =
|
||||
}
|
||||
| {
|
||||
agentBase: GetAgentSessionResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateSession>
|
||||
update: ReturnType<typeof useUpdateSession>['updateSession']
|
||||
}
|
||||
|
||||
const defaultConfiguration: AgentConfigurationState = AgentConfigurationSchema.parse({})
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { Avatar } from '@heroui/react'
|
||||
import { getAgentTypeAvatar } from '@renderer/config/agent'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { getAgentTypeLabel } from '@renderer/i18n/label'
|
||||
import { GetAgentResponse } from '@renderer/types'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { AccessibleDirsSetting } from './AccessibleDirsSetting'
|
||||
import { AvatarSetting } from './AvatarSetting'
|
||||
import { DescriptionSetting } from './DescriptionSetting'
|
||||
import { ModelSetting } from './ModelSetting'
|
||||
import { NameSetting } from './NameSetting'
|
||||
import { SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
||||
|
||||
// const logger = loggerService.withContext('AgentEssentialSettings')
|
||||
|
||||
interface AgentEssentialSettingsProps {
|
||||
agent: GetAgentResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateAgent>['updateAgent']
|
||||
showModelSetting?: boolean
|
||||
}
|
||||
|
||||
const AgentEssentialSettings: FC<AgentEssentialSettingsProps> = ({ agent, update, showModelSetting = true }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (!agent) return null
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<SettingsItem inline>
|
||||
<SettingsTitle>{t('agent.type.label')}</SettingsTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar src={getAgentTypeAvatar(agent.type)} className="h-6 w-6 text-lg" />
|
||||
<span>{(agent?.name ?? agent?.type) ? getAgentTypeLabel(agent.type) : ''}</span>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
<AvatarSetting agent={agent} update={update} />
|
||||
<NameSetting base={agent} update={update} />
|
||||
{showModelSetting && <ModelSetting base={agent} update={update} />}
|
||||
<AccessibleDirsSetting base={agent} update={update} />
|
||||
<DescriptionSetting base={agent} update={update} />
|
||||
</SettingsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentEssentialSettings
|
||||
@ -6,7 +6,7 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AdvancedSettings from './AdvancedSettings'
|
||||
import AgentEssentialSettings from './AgentEssentialSettings'
|
||||
import EssentialSettings from './EssentialSettings'
|
||||
import PromptSettings from './PromptSettings'
|
||||
import { AgentLabel, LeftMenu, Settings, StyledMenu, StyledModal } from './shared'
|
||||
import ToolingSettings from './ToolingSettings'
|
||||
@ -87,7 +87,7 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
/>
|
||||
</LeftMenu>
|
||||
<Settings>
|
||||
{menu === 'essential' && <AgentEssentialSettings agent={agent} update={updateAgent} />}
|
||||
{menu === 'essential' && <EssentialSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'prompt' && <PromptSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'tooling' && <ToolingSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'advanced' && <AdvancedSettings agentBase={agent} update={updateAgent} />}
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
import { Avatar } from '@heroui/react'
|
||||
import { getAgentTypeAvatar } from '@renderer/config/agent'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { useUpdateSession } from '@renderer/hooks/agents/useUpdateSession'
|
||||
import { getAgentTypeLabel } from '@renderer/i18n/label'
|
||||
import { GetAgentResponse, GetAgentSessionResponse, isAgentEntity } from '@renderer/types'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { AccessibleDirsSetting } from './AccessibleDirsSetting'
|
||||
import { AvatarSetting } from './AvatarSetting'
|
||||
import { DescriptionSetting } from './DescriptionSetting'
|
||||
import { ModelSetting } from './ModelSetting'
|
||||
import { NameSetting } from './NameSetting'
|
||||
import { SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
||||
|
||||
// const logger = loggerService.withContext('AgentEssentialSettings')
|
||||
|
||||
type EssentialSettingsProps =
|
||||
| {
|
||||
agentBase: GetAgentResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateAgent>['updateAgent']
|
||||
}
|
||||
| {
|
||||
agentBase: GetAgentSessionResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateSession>['updateSession']
|
||||
}
|
||||
|
||||
const EssentialSettings: FC<EssentialSettingsProps> = ({ agentBase, update }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (!agentBase) return null
|
||||
|
||||
const isAgent = isAgentEntity(agentBase)
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
{isAgent && (
|
||||
<SettingsItem inline>
|
||||
<SettingsTitle>{t('agent.type.label')}</SettingsTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar src={getAgentTypeAvatar(agentBase.type)} className="h-6 w-6 text-lg" />
|
||||
<span>{(agentBase?.name ?? agentBase?.type) ? getAgentTypeLabel(agentBase.type) : ''}</span>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
)}
|
||||
{isAgent && <AvatarSetting agent={agentBase} update={update} />}
|
||||
<NameSetting base={agentBase} update={update} />
|
||||
<ModelSetting base={agentBase} update={update} />
|
||||
<AccessibleDirsSetting base={agentBase} update={update} />
|
||||
<DescriptionSetting base={agentBase} update={update} />
|
||||
</SettingsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default EssentialSettings
|
||||
@ -1,4 +1,4 @@
|
||||
import SelectAgentModelButton from '@renderer/pages/home/components/SelectAgentModelButton'
|
||||
import SelectAgentBaseModelButton from '@renderer/pages/home/components/SelectAgentBaseModelButton'
|
||||
import { AgentBaseWithId, ApiModel, UpdateAgentBaseForm } from '@renderer/types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@ -21,9 +21,9 @@ export const ModelSetting: React.FC<ModelSettingProps> = ({ base, update, isDisa
|
||||
if (!base) return null
|
||||
|
||||
return (
|
||||
<SettingsItem inline className="gap-8">
|
||||
<SettingsItem inline>
|
||||
<SettingsTitle id="model">{t('common.model')}</SettingsTitle>
|
||||
<SelectAgentModelButton agent={base} onSelect={updateModel} isDisabled={isDisabled} />
|
||||
<SelectAgentBaseModelButton agentBase={base} onSelect={updateModel} isDisabled={isDisabled} />
|
||||
</SettingsItem>
|
||||
)
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ type AgentPromptSettingsProps =
|
||||
}
|
||||
| {
|
||||
agentBase: AgentSessionEntity | undefined | null
|
||||
update: ReturnType<typeof useUpdateSession>
|
||||
update: ReturnType<typeof useUpdateSession>['updateSession']
|
||||
}
|
||||
|
||||
const PromptSettings: FC<AgentPromptSettingsProps> = ({ agentBase, update }) => {
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import { GetAgentSessionResponse } from '@renderer/types'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { AccessibleDirsSetting } from './AccessibleDirsSetting'
|
||||
import { DescriptionSetting } from './DescriptionSetting'
|
||||
import { NameSetting } from './NameSetting'
|
||||
import { SettingsContainer } from './shared'
|
||||
|
||||
// const logger = loggerService.withContext('AgentEssentialSettings')
|
||||
|
||||
interface SessionEssentialSettingsProps {
|
||||
session: GetAgentSessionResponse | undefined | null
|
||||
update: ReturnType<typeof useUpdateAgent>['updateAgent']
|
||||
}
|
||||
|
||||
const SessionEssentialSettings: FC<SessionEssentialSettingsProps> = ({ session, update }) => {
|
||||
if (!session) return null
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<NameSetting base={session} update={update} />
|
||||
<AccessibleDirsSetting base={session} update={update} />
|
||||
<DescriptionSetting base={session} update={update} />
|
||||
</SettingsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default SessionEssentialSettings
|
||||
@ -6,8 +6,8 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AdvancedSettings from './AdvancedSettings'
|
||||
import EssentialSettings from './EssentialSettings'
|
||||
import PromptSettings from './PromptSettings'
|
||||
import SessionEssentialSettings from './SessionEssentialSettings'
|
||||
import { LeftMenu, SessionLabel, Settings, StyledMenu, StyledModal } from './shared'
|
||||
import ToolingSettings from './ToolingSettings'
|
||||
|
||||
@ -30,7 +30,7 @@ const SessionSettingPopupContainer: React.FC<SessionSettingPopupParams> = ({ tab
|
||||
|
||||
const { session, isLoading, error } = useSession(agentId, sessionId)
|
||||
|
||||
const updateSession = useUpdateSession(agentId)
|
||||
const { updateSession } = useUpdateSession(agentId)
|
||||
|
||||
const onOk = () => {
|
||||
setOpen(false)
|
||||
@ -89,7 +89,7 @@ const SessionSettingPopupContainer: React.FC<SessionSettingPopupParams> = ({ tab
|
||||
/>
|
||||
</LeftMenu>
|
||||
<Settings>
|
||||
{menu === 'essential' && <SessionEssentialSettings session={session} update={updateSession} />}
|
||||
{menu === 'essential' && <EssentialSettings agentBase={session} update={updateSession} />}
|
||||
{menu === 'prompt' && <PromptSettings agentBase={session} update={updateSession} />}
|
||||
{menu === 'tooling' && <ToolingSettings agentBase={session} update={updateSession} />}
|
||||
{menu === 'advanced' && <AdvancedSettings agentBase={session} update={updateSession} />}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { cn } from '@heroui/react'
|
||||
import Ellipsis from '@renderer/components/Ellipsis'
|
||||
import EmojiIcon from '@renderer/components/EmojiIcon'
|
||||
import { getAgentTypeLabel } from '@renderer/i18n/label'
|
||||
import { AgentEntity, AgentSessionEntity } from '@renderer/types'
|
||||
@ -35,11 +34,11 @@ export const AgentLabel: React.FC<AgentLabelProps> = ({ agent, classNames }) =>
|
||||
const emoji = agent?.configuration?.avatar
|
||||
|
||||
return (
|
||||
<div className={cn('flex w-full items-center gap-2', classNames?.container)}>
|
||||
<div className={cn('flex w-full items-center gap-2 truncate', classNames?.container)}>
|
||||
<EmojiIcon emoji={emoji || '⭐️'} className={classNames?.avatar} />
|
||||
<Ellipsis className={classNames?.name}>
|
||||
<span className={cn('truncate', classNames?.name)}>
|
||||
{agent?.name ?? (agent?.type ? getAgentTypeLabel(agent.type) : '')}
|
||||
</Ellipsis>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -53,7 +52,7 @@ export const SessionLabel: React.FC<SessionLabelProps> = ({ session, className }
|
||||
const displayName = session?.name ?? session?.id
|
||||
return (
|
||||
<>
|
||||
<span className={cn('px-2 text-sm', className)}>{displayName}</span>
|
||||
<span className={cn('truncate px-2 text-sm', className)}>{displayName}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ export interface ChatState {
|
||||
activeAgentId: string | null
|
||||
/** UI state. Map agent id to active session id.
|
||||
* null represents no active session */
|
||||
activeSessionId: Record<string, string | null>
|
||||
activeSessionIdMap: Record<string, string | null>
|
||||
/** meanwhile active Assistants or Agents */
|
||||
activeTopicOrSession: 'topic' | 'session'
|
||||
/** topic ids that are currently being renamed */
|
||||
@ -90,7 +90,7 @@ const initialState: RuntimeState = {
|
||||
activeTopic: null,
|
||||
activeAgentId: null,
|
||||
activeTopicOrSession: 'topic',
|
||||
activeSessionId: {},
|
||||
activeSessionIdMap: {},
|
||||
renamingTopics: [],
|
||||
newlyRenamedTopics: [],
|
||||
sessionWaiting: {}
|
||||
@ -163,7 +163,7 @@ const runtimeSlice = createSlice({
|
||||
},
|
||||
setActiveSessionIdAction: (state, action: PayloadAction<{ agentId: string; sessionId: string | null }>) => {
|
||||
const { agentId, sessionId } = action.payload
|
||||
state.chat.activeSessionId[agentId] = sessionId
|
||||
state.chat.activeSessionIdMap[agentId] = sessionId
|
||||
},
|
||||
setActiveTopicOrSessionAction: (state, action: PayloadAction<'topic' | 'session'>) => {
|
||||
state.chat.activeTopicOrSession = action.payload
|
||||
|
||||
@ -266,8 +266,12 @@ export const GetAgentSessionResponseSchema = AgentSessionEntitySchema.extend({
|
||||
slash_commands: z.array(SlashCommandSchema).optional() // Array of slash commands to trigger the agent
|
||||
})
|
||||
|
||||
export const CreateAgentSessionResponseSchema = GetAgentSessionResponseSchema
|
||||
|
||||
export type GetAgentSessionResponse = z.infer<typeof GetAgentSessionResponseSchema>
|
||||
|
||||
export type CreateAgentSessionResponse = GetAgentSessionResponse
|
||||
|
||||
export const ListAgentSessionsResponseSchema = z.object({
|
||||
data: z.array(AgentSessionEntitySchema),
|
||||
total: z.int(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user