feat: switch topic
This commit is contained in:
parent
2b4c4f46e6
commit
4b17e4cd16
@ -1,12 +1,12 @@
|
|||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import {
|
import {
|
||||||
addConversation as _addConversation,
|
addTopic as _addTopic,
|
||||||
removeConversation as _removeConversation,
|
removeTopic as _removeTopic,
|
||||||
addAgent,
|
addAgent,
|
||||||
removeAgent,
|
removeAgent,
|
||||||
updateAgent
|
updateAgent
|
||||||
} from '@renderer/store/agents'
|
} from '@renderer/store/agents'
|
||||||
import { Agent, Conversation } from '@renderer/types'
|
import { Agent, Topic } from '@renderer/types'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
|
|
||||||
export default function useAgents() {
|
export default function useAgents() {
|
||||||
@ -20,7 +20,7 @@ export default function useAgents() {
|
|||||||
dispatch(removeAgent({ id }))
|
dispatch(removeAgent({ id }))
|
||||||
const agent = agents.find((a) => a.id === id)
|
const agent = agents.find((a) => a.id === id)
|
||||||
if (agent) {
|
if (agent) {
|
||||||
agent.conversations.forEach((id) => localforage.removeItem(`conversation:${id}`))
|
agent.topics.forEach((id) => localforage.removeItem(`topic:${id}`))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateAgent: (agent: Agent) => dispatch(updateAgent(agent))
|
updateAgent: (agent: Agent) => dispatch(updateAgent(agent))
|
||||||
@ -33,11 +33,11 @@ export function useAgent(id: string) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
agent,
|
agent,
|
||||||
addConversation: (conversation: Conversation) => {
|
addTopic: (topic: Topic) => {
|
||||||
dispatch(_addConversation({ agentId: agent?.id!, conversation }))
|
dispatch(_addTopic({ agentId: agent?.id!, topic }))
|
||||||
},
|
},
|
||||||
removeConversation: (conversation: Conversation) => {
|
removeTopic: (topic: Topic) => {
|
||||||
dispatch(_removeConversation({ agentId: agent?.id!, conversation }))
|
dispatch(_removeTopic({ agentId: agent?.id!, topic }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
import { FC } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import Inputbar from './Inputbar'
|
import Inputbar from './Inputbar'
|
||||||
import Conversations from './Conversations'
|
import Conversations from './Conversations'
|
||||||
@ -13,6 +13,7 @@ interface Props {
|
|||||||
|
|
||||||
const Chat: FC<Props> = (props) => {
|
const Chat: FC<Props> = (props) => {
|
||||||
const { agent } = useAgent(props.agent.id)
|
const { agent } = useAgent(props.agent.id)
|
||||||
|
const [activeTopic, setActiveTopic] = useState(agent.topics[0])
|
||||||
|
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
return null
|
return null
|
||||||
@ -21,10 +22,10 @@ const Chat: FC<Props> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Container id="chat">
|
<Container id="chat">
|
||||||
<Flex vertical flex={1} justify="space-between">
|
<Flex vertical flex={1} justify="space-between">
|
||||||
<Conversations agent={agent} />
|
<Conversations agent={agent} topic={activeTopic} />
|
||||||
<Inputbar agent={agent} />
|
<Inputbar agent={agent} setActiveTopic={setActiveTopic} />
|
||||||
</Flex>
|
</Flex>
|
||||||
<TopicList agent={agent} />
|
<TopicList agent={agent} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { openaiProvider } from '@renderer/services/provider'
|
import { openaiProvider } from '@renderer/services/provider'
|
||||||
import { Agent, Conversation, Message } from '@renderer/types'
|
import { Agent, Message, Topic } from '@renderer/types'
|
||||||
import { runAsyncFunction, uuid } from '@renderer/utils'
|
import { runAsyncFunction, uuid } from '@renderer/utils'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { FC, useCallback, useEffect, useState } from 'react'
|
import { FC, useCallback, useEffect, useState } from 'react'
|
||||||
@ -11,28 +11,29 @@ import hljs from 'highlight.js'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agent: Agent
|
agent: Agent
|
||||||
|
topic: Topic
|
||||||
}
|
}
|
||||||
|
|
||||||
const Conversations: FC<Props> = ({ agent }) => {
|
const Conversations: FC<Props> = ({ agent, topic }) => {
|
||||||
const [messages, setMessages] = useState<Message[]>([])
|
const [messages, setMessages] = useState<Message[]>([])
|
||||||
const [lastMessage, setLastMessage] = useState<Message | null>(null)
|
const [lastMessage, setLastMessage] = useState<Message | null>(null)
|
||||||
|
|
||||||
const { id: conversationId } = agent.conversations[0]
|
const { id: topicId } = topic
|
||||||
|
|
||||||
const onSendMessage = useCallback(
|
const onSendMessage = useCallback(
|
||||||
(message: Message) => {
|
(message: Message) => {
|
||||||
const _messages = [...messages, message]
|
const _messages = [...messages, message]
|
||||||
setMessages(_messages)
|
setMessages(_messages)
|
||||||
|
|
||||||
const conversation = {
|
const topic = {
|
||||||
id: conversationId,
|
id: topicId,
|
||||||
name: 'Default Topic',
|
name: 'Default Topic',
|
||||||
messages: _messages
|
messages: _messages
|
||||||
}
|
}
|
||||||
|
|
||||||
localforage.setItem<Conversation>(`conversation:${conversationId}`, conversation)
|
localforage.setItem<Topic>(`topic:${topicId}`, topic)
|
||||||
},
|
},
|
||||||
[conversationId, messages]
|
[topicId, messages]
|
||||||
)
|
)
|
||||||
|
|
||||||
const fetchChatCompletion = useCallback(
|
const fetchChatCompletion = useCallback(
|
||||||
@ -48,7 +49,7 @@ const Conversations: FC<Props> = ({ agent }) => {
|
|||||||
role: 'agent',
|
role: 'agent',
|
||||||
content: '',
|
content: '',
|
||||||
agentId: agent.id,
|
agentId: agent.id,
|
||||||
conversationId,
|
topicId,
|
||||||
createdAt: 'now'
|
createdAt: 'now'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ const Conversations: FC<Props> = ({ agent }) => {
|
|||||||
|
|
||||||
return _message
|
return _message
|
||||||
},
|
},
|
||||||
[agent.id, conversationId]
|
[agent.id, topicId]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,15 +85,15 @@ const Conversations: FC<Props> = ({ agent }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
runAsyncFunction(async () => {
|
runAsyncFunction(async () => {
|
||||||
const conversation = await localforage.getItem<Conversation>(`conversation:${conversationId}`)
|
const topic = await localforage.getItem<Topic>(`topic:${topicId}`)
|
||||||
setMessages(conversation ? conversation.messages : [])
|
setMessages(topic ? topic.messages : [])
|
||||||
})
|
})
|
||||||
}, [conversationId])
|
}, [topicId])
|
||||||
|
|
||||||
useEffect(() => hljs.highlightAll())
|
useEffect(() => hljs.highlightAll())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container id="conversations">
|
<Container id="topics">
|
||||||
{lastMessage && <MessageItem message={lastMessage} />}
|
{lastMessage && <MessageItem message={lastMessage} />}
|
||||||
{reverse([...messages]).map((message) => (
|
{reverse([...messages]).map((message) => (
|
||||||
<MessageItem message={message} key={message.id} />
|
<MessageItem message={message} key={message.id} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Agent, Conversation, Message } from '@renderer/types'
|
import { Agent, Message, Topic } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -10,23 +10,24 @@ import { useAgent } from '@renderer/hooks/useAgents'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agent: Agent
|
agent: Agent
|
||||||
|
setActiveTopic: (topic: Topic) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Inputbar: FC<Props> = ({ agent }) => {
|
const Inputbar: FC<Props> = ({ agent, setActiveTopic }) => {
|
||||||
const [text, setText] = useState('')
|
const [text, setText] = useState('')
|
||||||
const { setShowRightSidebar } = useShowRightSidebar()
|
const { setShowRightSidebar } = useShowRightSidebar()
|
||||||
const { addConversation } = useAgent(agent.id)
|
const { addTopic } = useAgent(agent.id)
|
||||||
|
|
||||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
const conversationId = agent.conversations[0] ? agent.conversations[0] : uuid()
|
const topicId = agent.topics[0] ? agent.topics[0] : uuid()
|
||||||
|
|
||||||
const message: Message = {
|
const message: Message = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: text,
|
content: text,
|
||||||
agentId: agent.id,
|
agentId: agent.id,
|
||||||
conversationId,
|
topicId,
|
||||||
createdAt: 'now'
|
createdAt: 'now'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +39,13 @@ const Inputbar: FC<Props> = ({ agent }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addNewConversation = () => {
|
const addNewConversation = () => {
|
||||||
const conversation: Conversation = {
|
const topic: Topic = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: 'Default Topic',
|
name: 'Default Topic',
|
||||||
messages: []
|
messages: []
|
||||||
}
|
}
|
||||||
addConversation(conversation)
|
addTopic(topic)
|
||||||
|
setActiveTopic(topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent, Topic } from '@renderer/types'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agent: Agent
|
agent: Agent
|
||||||
|
activeTopic: Topic
|
||||||
|
setActiveTopic: (topic: Topic) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TopicList: FC<Props> = ({ agent }) => {
|
const TopicList: FC<Props> = ({ agent, activeTopic, setActiveTopic }) => {
|
||||||
const { showRightSidebar } = useShowRightSidebar()
|
const { showRightSidebar } = useShowRightSidebar()
|
||||||
const [activeTopic, setActiveTopic] = useState(agent.conversations[0])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setActiveTopic(agent.conversations[0])
|
|
||||||
}, [agent.conversations, agent.id])
|
|
||||||
|
|
||||||
if (!showRightSidebar) {
|
if (!showRightSidebar) {
|
||||||
return null
|
return null
|
||||||
@ -21,7 +18,7 @@ const TopicList: FC<Props> = ({ agent }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={showRightSidebar ? '' : 'collapsed'}>
|
<Container className={showRightSidebar ? '' : 'collapsed'}>
|
||||||
{agent.conversations.map((topic) => (
|
{agent.topics.map((topic) => (
|
||||||
<TopicListItem
|
<TopicListItem
|
||||||
key={topic.id}
|
key={topic.id}
|
||||||
className={topic.id === activeTopic?.id ? 'active' : ''}
|
className={topic.id === activeTopic?.id ? 'active' : ''}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export function getDefaultAgent(): Agent {
|
|||||||
id: 'default',
|
id: 'default',
|
||||||
name: 'Default Agent',
|
name: 'Default Agent',
|
||||||
description: "Hello, I'm Default Agent.",
|
description: "Hello, I'm Default Agent.",
|
||||||
conversations: [
|
topics: [
|
||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: 'Default Topic',
|
name: 'Default Topic',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { getDefaultAgent } from '@renderer/services/agent'
|
import { getDefaultAgent } from '@renderer/services/agent'
|
||||||
import { Agent, Conversation } from '@renderer/types'
|
import { Agent, Topic } from '@renderer/types'
|
||||||
import { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
|
|
||||||
export interface AgentsState {
|
export interface AgentsState {
|
||||||
@ -24,23 +24,23 @@ const agentsSlice = createSlice({
|
|||||||
updateAgent: (state, action: PayloadAction<Agent>) => {
|
updateAgent: (state, action: PayloadAction<Agent>) => {
|
||||||
state.agents = state.agents.map((c) => (c.id === action.payload.id ? action.payload : c))
|
state.agents = state.agents.map((c) => (c.id === action.payload.id ? action.payload : c))
|
||||||
},
|
},
|
||||||
addConversation: (state, action: PayloadAction<{ agentId: string; conversation: Conversation }>) => {
|
addTopic: (state, action: PayloadAction<{ agentId: string; topic: Topic }>) => {
|
||||||
console.debug(action.payload)
|
console.debug(action.payload)
|
||||||
state.agents = state.agents.map((agent) =>
|
state.agents = state.agents.map((agent) =>
|
||||||
agent.id === action.payload.agentId
|
agent.id === action.payload.agentId
|
||||||
? {
|
? {
|
||||||
...agent,
|
...agent,
|
||||||
conversations: uniqBy([action.payload.conversation, ...agent.conversations], 'id')
|
topics: uniqBy([action.payload.topic, ...agent.topics], 'id')
|
||||||
}
|
}
|
||||||
: agent
|
: agent
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
removeConversation: (state, action: PayloadAction<{ agentId: string; conversation: Conversation }>) => {
|
removeTopic: (state, action: PayloadAction<{ agentId: string; topic: Topic }>) => {
|
||||||
state.agents = state.agents.map((agent) =>
|
state.agents = state.agents.map((agent) =>
|
||||||
agent.id === action.payload.agentId
|
agent.id === action.payload.agentId
|
||||||
? {
|
? {
|
||||||
...agent,
|
...agent,
|
||||||
conversations: agent.conversations.filter(({ id }) => id !== action.payload.conversation.id)
|
topics: agent.topics.filter(({ id }) => id !== action.payload.topic.id)
|
||||||
}
|
}
|
||||||
: agent
|
: agent
|
||||||
)
|
)
|
||||||
@ -48,6 +48,6 @@ const agentsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { addAgent, removeAgent, updateAgent, addConversation, removeConversation } = agentsSlice.actions
|
export const { addAgent, removeAgent, updateAgent, addTopic, removeTopic } = agentsSlice.actions
|
||||||
|
|
||||||
export default agentsSlice.reducer
|
export default agentsSlice.reducer
|
||||||
|
|||||||
@ -2,7 +2,7 @@ export type Agent = {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
conversations: Conversation[]
|
topics: Topic[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Message = {
|
export type Message = {
|
||||||
@ -10,11 +10,11 @@ export type Message = {
|
|||||||
role: 'user' | 'agent'
|
role: 'user' | 'agent'
|
||||||
content: string
|
content: string
|
||||||
agentId: string
|
agentId: string
|
||||||
conversationId: string
|
topicId: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Conversation = {
|
export type Topic = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user