mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-05-02 06:56:29 +00:00
feat: add agent timestamps and improve agent retrieval logic
This commit is contained in:
@@ -90,14 +90,23 @@ def get_agent_key(agent_id, user_id):
|
|||||||
if not agent_id:
|
if not agent_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
agent = agents_collection.find_one({"_id": ObjectId(agent_id)})
|
try:
|
||||||
if agent is None:
|
agent = agents_collection.find_one({"_id": ObjectId(agent_id)})
|
||||||
raise Exception("Agent not found", 404)
|
if agent is None:
|
||||||
|
raise Exception("Agent not found", 404)
|
||||||
|
|
||||||
if agent.get("is_public") or agent.get("user") == user_id:
|
if agent.get("user") == user_id:
|
||||||
return str(agent["key"])
|
agents_collection.update_one(
|
||||||
|
{"_id": ObjectId(agent_id)},
|
||||||
|
{"$set": {"lastUsedAt": datetime.datetime.now(datetime.timezone.utc)}},
|
||||||
|
)
|
||||||
|
return str(agent["key"])
|
||||||
|
|
||||||
raise Exception("Unauthorized access to the agent", 403)
|
raise Exception("Unauthorized access to the agent", 403)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in get_agent_key: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_data_from_api_key(api_key):
|
def get_data_from_api_key(api_key):
|
||||||
|
|||||||
@@ -970,6 +970,9 @@ class GetAgent(Resource):
|
|||||||
"tools": agent.get("tools", []),
|
"tools": agent.get("tools", []),
|
||||||
"agent_type": agent["agent_type"],
|
"agent_type": agent["agent_type"],
|
||||||
"status": agent["status"],
|
"status": agent["status"],
|
||||||
|
"createdAt": agent["createdAt"],
|
||||||
|
"updatedAt": agent["updatedAt"],
|
||||||
|
"lastUsedAt": agent["lastUsedAt"],
|
||||||
"key": f"{agent['key'][:4]}...{agent['key'][-4:]}",
|
"key": f"{agent['key'][:4]}...{agent['key'][-4:]}",
|
||||||
}
|
}
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@@ -1005,6 +1008,9 @@ class GetAgents(Resource):
|
|||||||
"tools": agent.get("tools", []),
|
"tools": agent.get("tools", []),
|
||||||
"agent_type": agent["agent_type"],
|
"agent_type": agent["agent_type"],
|
||||||
"status": agent["status"],
|
"status": agent["status"],
|
||||||
|
"created_at": agent["createdAt"],
|
||||||
|
"updated_at": agent["updatedAt"],
|
||||||
|
"last_used_at": agent["lastUsedAt"],
|
||||||
"key": f"{agent['key'][:4]}...{agent['key'][-4:]}",
|
"key": f"{agent['key'][:4]}...{agent['key'][-4:]}",
|
||||||
}
|
}
|
||||||
for agent in agents
|
for agent in agents
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
setConversations,
|
setConversations,
|
||||||
setModalStateDeleteConv,
|
setModalStateDeleteConv,
|
||||||
setSelectedAgent,
|
setSelectedAgent,
|
||||||
|
setAgents,
|
||||||
} from './preferences/preferenceSlice';
|
} from './preferences/preferenceSlice';
|
||||||
import Upload from './upload/Upload';
|
import Upload from './upload/Upload';
|
||||||
|
|
||||||
@@ -90,9 +91,17 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
|||||||
async function getAgents() {
|
async function getAgents() {
|
||||||
const response = await userService.getAgents(token);
|
const response = await userService.getAgents(token);
|
||||||
if (!response.ok) throw new Error('Failed to fetch agents');
|
if (!response.ok) throw new Error('Failed to fetch agents');
|
||||||
const data = await response.json();
|
const data: Agent[] = await response.json();
|
||||||
|
dispatch(setAgents(data));
|
||||||
setRecentAgents(
|
setRecentAgents(
|
||||||
data.filter((agent: Agent) => agent.status === 'published'),
|
data
|
||||||
|
.filter((agent: Agent) => agent.status === 'published')
|
||||||
|
.sort(
|
||||||
|
(a: Agent, b: Agent) =>
|
||||||
|
new Date(b.last_used_at ?? 0).getTime() -
|
||||||
|
new Date(a.last_used_at ?? 0).getTime(),
|
||||||
|
)
|
||||||
|
.slice(0, 3),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,7 +365,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4"
|
className="mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4 hover:bg-bright-gray dark:hover:bg-dark-charcoal"
|
||||||
onClick={() => navigate('/agents')}
|
onClick={() => navigate('/agents')}
|
||||||
>
|
>
|
||||||
<div className="flex w-6 justify-center">
|
<div className="flex w-6 justify-center">
|
||||||
@@ -366,7 +375,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
|||||||
className="h-[18px] w-[18px]"
|
className="h-[18px] w-[18px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap text-sm leading-6 text-eerie-black hover:text-purple-30 dark:text-bright-gray hover:dark:text-purple-30">
|
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap text-sm leading-6 text-eerie-black dark:text-bright-gray">
|
||||||
Manage Agents
|
Manage Agents
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
import React, { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { Route, Routes, useNavigate } from 'react-router-dom';
|
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import userService from '../api/services/userService';
|
import userService from '../api/services/userService';
|
||||||
@@ -12,10 +12,11 @@ import ThreeDots from '../assets/three-dots.svg';
|
|||||||
import ContextMenu, { MenuOption } from '../components/ContextMenu';
|
import ContextMenu, { MenuOption } from '../components/ContextMenu';
|
||||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||||
import { ActiveState } from '../models/misc';
|
import { ActiveState } from '../models/misc';
|
||||||
import { selectToken } from '../preferences/preferenceSlice';
|
import { selectToken, setSelectedAgent } from '../preferences/preferenceSlice';
|
||||||
import AgentLogs from './AgentLogs';
|
import AgentLogs from './AgentLogs';
|
||||||
import NewAgent from './NewAgent';
|
import NewAgent from './NewAgent';
|
||||||
import { Agent } from './types';
|
import { Agent } from './types';
|
||||||
|
import Spinner from '../components/Spinner';
|
||||||
|
|
||||||
export default function Agents() {
|
export default function Agents() {
|
||||||
return (
|
return (
|
||||||
@@ -33,14 +34,23 @@ function AgentsList() {
|
|||||||
const token = useSelector(selectToken);
|
const token = useSelector(selectToken);
|
||||||
|
|
||||||
const [userAgents, setUserAgents] = useState<Agent[]>([]);
|
const [userAgents, setUserAgents] = useState<Agent[]>([]);
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
useEffect(() => {
|
const getAgents = async () => {
|
||||||
const getAgents = async () => {
|
try {
|
||||||
|
setLoading(true);
|
||||||
const response = await userService.getAgents(token);
|
const response = await userService.getAgents(token);
|
||||||
if (!response.ok) throw new Error('Failed to fetch agents');
|
if (!response.ok) throw new Error('Failed to fetch agents');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setUserAgents(data);
|
setUserAgents(data);
|
||||||
};
|
setLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
getAgents();
|
getAgents();
|
||||||
}, [token]);
|
}, [token]);
|
||||||
return (
|
return (
|
||||||
@@ -107,9 +117,29 @@ function AgentsList() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-wrap gap-4">
|
<div className="flex w-full flex-wrap gap-4">
|
||||||
{userAgents.map((agent, idx) => (
|
{loading ? (
|
||||||
<AgentCard key={idx} agent={agent} setUserAgents={setUserAgents} />
|
<div className="flex h-72 w-full items-center justify-center">
|
||||||
))}
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
) : userAgents.length > 0 ? (
|
||||||
|
userAgents.map((agent) => (
|
||||||
|
<AgentCard
|
||||||
|
key={agent.id}
|
||||||
|
agent={agent}
|
||||||
|
setUserAgents={setUserAgents}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="flex h-72 w-full flex-col items-center justify-center gap-3 text-base text-[#18181B] dark:text-[#E0E0E0]">
|
||||||
|
<p>You don’t have any created agents yet </p>
|
||||||
|
<button
|
||||||
|
className="ml-2 rounded-full bg-purple-30 px-4 py-2 text-sm text-white hover:bg-violets-are-blue"
|
||||||
|
onClick={() => navigate('/agents/new')}
|
||||||
|
>
|
||||||
|
New Agent
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,12 +154,15 @@ function AgentCard({
|
|||||||
setUserAgents: React.Dispatch<React.SetStateAction<Agent[]>>;
|
setUserAgents: React.Dispatch<React.SetStateAction<Agent[]>>;
|
||||||
}) {
|
}) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const token = useSelector(selectToken);
|
const token = useSelector(selectToken);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
|
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
|
||||||
const [deleteConfirmation, setDeleteConfirmation] =
|
const [deleteConfirmation, setDeleteConfirmation] =
|
||||||
useState<ActiveState>('INACTIVE');
|
useState<ActiveState>('INACTIVE');
|
||||||
|
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const menuOptions: MenuOption[] = [
|
const menuOptions: MenuOption[] = [
|
||||||
{
|
{
|
||||||
icon: Monitoring,
|
icon: Monitoring,
|
||||||
@@ -156,13 +189,21 @@ function AgentCard({
|
|||||||
{
|
{
|
||||||
icon: Trash,
|
icon: Trash,
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onClick: () => setDeleteConfirmation('ACTIVE'),
|
onClick: (e: SyntheticEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setDeleteConfirmation('ACTIVE');
|
||||||
|
},
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
iconWidth: 12,
|
iconWidth: 12,
|
||||||
iconHeight: 12,
|
iconHeight: 12,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
dispatch(setSelectedAgent(agent));
|
||||||
|
navigate(`/`);
|
||||||
|
};
|
||||||
|
|
||||||
const handleDelete = async (agentId: string) => {
|
const handleDelete = async (agentId: string) => {
|
||||||
const response = await userService.deleteAgent(agentId, token);
|
const response = await userService.deleteAgent(agentId, token);
|
||||||
if (!response.ok) throw new Error('Failed to delete agent');
|
if (!response.ok) throw new Error('Failed to delete agent');
|
||||||
@@ -172,13 +213,17 @@ function AgentCard({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-44 w-48 flex-col justify-between rounded-[1.2rem] bg-[#F6F6F6] px-6 py-5 dark:bg-[#383838]">
|
<div
|
||||||
|
className="relative flex h-44 w-48 cursor-pointer flex-col justify-between rounded-[1.2rem] bg-[#F6F6F6] px-6 py-5 dark:bg-[#383838]"
|
||||||
|
onClick={(e) => handleClick()}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
setIsMenuOpen(true);
|
setIsMenuOpen(true);
|
||||||
}}
|
}}
|
||||||
className="absolute right-4 top-4 cursor-pointer"
|
className="absolute right-4 top-4 z-50 cursor-pointer"
|
||||||
>
|
>
|
||||||
<img src={ThreeDots} alt={'use-agent'} className="h-[19px] w-[19px]" />
|
<img src={ThreeDots} alt={'use-agent'} className="h-[19px] w-[19px]" />
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
|
|||||||
@@ -11,4 +11,7 @@ export type Agent = {
|
|||||||
agent_type: string;
|
agent_type: string;
|
||||||
status: string;
|
status: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
created_at?: string;
|
||||||
|
updated_at?: string;
|
||||||
|
last_used_at?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface Preference {
|
|||||||
token: string | null;
|
token: string | null;
|
||||||
modalState: ActiveState;
|
modalState: ActiveState;
|
||||||
paginatedDocuments: Doc[] | null;
|
paginatedDocuments: Doc[] | null;
|
||||||
|
agents: Agent[] | null;
|
||||||
selectedAgent: Agent | null;
|
selectedAgent: Agent | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ const initialState: Preference = {
|
|||||||
token: localStorage.getItem('authToken') || null,
|
token: localStorage.getItem('authToken') || null,
|
||||||
modalState: 'INACTIVE',
|
modalState: 'INACTIVE',
|
||||||
paginatedDocuments: null,
|
paginatedDocuments: null,
|
||||||
|
agents: null,
|
||||||
selectedAgent: null,
|
selectedAgent: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,6 +88,9 @@ export const prefSlice = createSlice({
|
|||||||
setModalStateDeleteConv: (state, action: PayloadAction<ActiveState>) => {
|
setModalStateDeleteConv: (state, action: PayloadAction<ActiveState>) => {
|
||||||
state.modalState = action.payload;
|
state.modalState = action.payload;
|
||||||
},
|
},
|
||||||
|
setAgents: (state, action) => {
|
||||||
|
state.agents = action.payload;
|
||||||
|
},
|
||||||
setSelectedAgent: (state, action) => {
|
setSelectedAgent: (state, action) => {
|
||||||
state.selectedAgent = action.payload;
|
state.selectedAgent = action.payload;
|
||||||
},
|
},
|
||||||
@@ -103,6 +108,7 @@ export const {
|
|||||||
setTokenLimit,
|
setTokenLimit,
|
||||||
setModalStateDeleteConv,
|
setModalStateDeleteConv,
|
||||||
setPaginatedDocuments,
|
setPaginatedDocuments,
|
||||||
|
setAgents,
|
||||||
setSelectedAgent,
|
setSelectedAgent,
|
||||||
} = prefSlice.actions;
|
} = prefSlice.actions;
|
||||||
export default prefSlice.reducer;
|
export default prefSlice.reducer;
|
||||||
@@ -178,5 +184,6 @@ export const selectTokenLimit = (state: RootState) =>
|
|||||||
state.preference.token_limit;
|
state.preference.token_limit;
|
||||||
export const selectPaginatedDocuments = (state: RootState) =>
|
export const selectPaginatedDocuments = (state: RootState) =>
|
||||||
state.preference.paginatedDocuments;
|
state.preference.paginatedDocuments;
|
||||||
|
export const selectAgents = (state: RootState) => state.preference.agents;
|
||||||
export const selectSelectedAgent = (state: RootState) =>
|
export const selectSelectedAgent = (state: RootState) =>
|
||||||
state.preference.selectedAgent;
|
state.preference.selectedAgent;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import { conversationSlice } from './conversation/conversationSlice';
|
import { conversationSlice } from './conversation/conversationSlice';
|
||||||
import { sharedConversationSlice } from './conversation/sharedConversationSlice';
|
import { sharedConversationSlice } from './conversation/sharedConversationSlice';
|
||||||
import {
|
import {
|
||||||
@@ -40,6 +41,7 @@ const preloadedState: { preference: Preference } = {
|
|||||||
],
|
],
|
||||||
modalState: 'INACTIVE',
|
modalState: 'INACTIVE',
|
||||||
paginatedDocuments: null,
|
paginatedDocuments: null,
|
||||||
|
agents: null,
|
||||||
selectedAgent: null,
|
selectedAgent: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user