mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-01-20 14:00:55 +00:00
* chore(dependabot): add react-widget npm dependency updates * refactor(prompts): init on load, mv to pref slice * (refactor): searchable dropdowns are separate * (fix/ui) prompts adjust * feat(changelog): dancing stars * (fix)conversation: re-blink bubble past stream * (fix)endless GET sources, esling err * (feat:Agents) folders metadata * (feat:agents) create new folder * (feat:agent-management) ui * feat:(agent folders) nesting/sub-folders * feat:(agent folders)- closer the figma, inline folder inputs * fix(delete behaviour) refetch agents on delete * (fix:search) folder context missing * fix(newAgent) preserve folder context * feat(agent folders) id preserved im query, navigate * feat(agents) mobile responsive * feat(search/agents) lookup for nested agents as well * (fix/modals) close on outside click --------- Co-authored-by: GH Action - Upstream Sync <action@github.com>
262 lines
11 KiB
Python
262 lines
11 KiB
Python
"""
|
|
Agent folders management routes.
|
|
Provides virtual folder organization for agents (Google Drive-like structure).
|
|
"""
|
|
|
|
import datetime
|
|
from bson.objectid import ObjectId
|
|
from flask import jsonify, make_response, request
|
|
from flask_restx import Namespace, Resource, fields
|
|
|
|
from application.api import api
|
|
from application.api.user.base import (
|
|
agent_folders_collection,
|
|
agents_collection,
|
|
)
|
|
|
|
agents_folders_ns = Namespace(
|
|
"agents_folders", description="Agent folder management", path="/api/agents/folders"
|
|
)
|
|
|
|
|
|
@agents_folders_ns.route("/")
|
|
class AgentFolders(Resource):
|
|
@api.doc(description="Get all folders for the user")
|
|
def get(self):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
try:
|
|
folders = list(agent_folders_collection.find({"user": user}))
|
|
result = [
|
|
{
|
|
"id": str(f["_id"]),
|
|
"name": f["name"],
|
|
"parent_id": f.get("parent_id"),
|
|
"created_at": f.get("created_at", "").isoformat() if f.get("created_at") else None,
|
|
"updated_at": f.get("updated_at", "").isoformat() if f.get("updated_at") else None,
|
|
}
|
|
for f in folders
|
|
]
|
|
return make_response(jsonify({"folders": result}), 200)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
@api.doc(description="Create a new folder")
|
|
@api.expect(
|
|
api.model(
|
|
"CreateFolder",
|
|
{
|
|
"name": fields.String(required=True, description="Folder name"),
|
|
"parent_id": fields.String(required=False, description="Parent folder ID"),
|
|
},
|
|
)
|
|
)
|
|
def post(self):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
data = request.get_json()
|
|
if not data or not data.get("name"):
|
|
return make_response(jsonify({"success": False, "message": "Folder name is required"}), 400)
|
|
|
|
parent_id = data.get("parent_id")
|
|
if parent_id:
|
|
parent = agent_folders_collection.find_one({"_id": ObjectId(parent_id), "user": user})
|
|
if not parent:
|
|
return make_response(jsonify({"success": False, "message": "Parent folder not found"}), 404)
|
|
|
|
try:
|
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
folder = {
|
|
"user": user,
|
|
"name": data["name"],
|
|
"parent_id": parent_id,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
}
|
|
result = agent_folders_collection.insert_one(folder)
|
|
return make_response(
|
|
jsonify({"id": str(result.inserted_id), "name": data["name"], "parent_id": parent_id}),
|
|
201,
|
|
)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
|
|
@agents_folders_ns.route("/<string:folder_id>")
|
|
class AgentFolder(Resource):
|
|
@api.doc(description="Get a specific folder with its agents")
|
|
def get(self, folder_id):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
try:
|
|
folder = agent_folders_collection.find_one({"_id": ObjectId(folder_id), "user": user})
|
|
if not folder:
|
|
return make_response(jsonify({"success": False, "message": "Folder not found"}), 404)
|
|
|
|
agents = list(agents_collection.find({"user": user, "folder_id": folder_id}))
|
|
agents_list = [
|
|
{"id": str(a["_id"]), "name": a["name"], "description": a.get("description", "")}
|
|
for a in agents
|
|
]
|
|
subfolders = list(agent_folders_collection.find({"user": user, "parent_id": folder_id}))
|
|
subfolders_list = [{"id": str(sf["_id"]), "name": sf["name"]} for sf in subfolders]
|
|
|
|
return make_response(
|
|
jsonify({
|
|
"id": str(folder["_id"]),
|
|
"name": folder["name"],
|
|
"parent_id": folder.get("parent_id"),
|
|
"agents": agents_list,
|
|
"subfolders": subfolders_list,
|
|
}),
|
|
200,
|
|
)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
@api.doc(description="Update a folder")
|
|
def put(self, folder_id):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
data = request.get_json()
|
|
if not data:
|
|
return make_response(jsonify({"success": False, "message": "No data provided"}), 400)
|
|
|
|
try:
|
|
update_fields = {"updated_at": datetime.datetime.now(datetime.timezone.utc)}
|
|
if "name" in data:
|
|
update_fields["name"] = data["name"]
|
|
if "parent_id" in data:
|
|
if data["parent_id"] == folder_id:
|
|
return make_response(jsonify({"success": False, "message": "Cannot set folder as its own parent"}), 400)
|
|
update_fields["parent_id"] = data["parent_id"]
|
|
|
|
result = agent_folders_collection.update_one(
|
|
{"_id": ObjectId(folder_id), "user": user}, {"$set": update_fields}
|
|
)
|
|
if result.matched_count == 0:
|
|
return make_response(jsonify({"success": False, "message": "Folder not found"}), 404)
|
|
return make_response(jsonify({"success": True}), 200)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
@api.doc(description="Delete a folder")
|
|
def delete(self, folder_id):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
try:
|
|
agents_collection.update_many(
|
|
{"user": user, "folder_id": folder_id}, {"$unset": {"folder_id": ""}}
|
|
)
|
|
agent_folders_collection.update_many(
|
|
{"user": user, "parent_id": folder_id}, {"$unset": {"parent_id": ""}}
|
|
)
|
|
result = agent_folders_collection.delete_one({"_id": ObjectId(folder_id), "user": user})
|
|
if result.deleted_count == 0:
|
|
return make_response(jsonify({"success": False, "message": "Folder not found"}), 404)
|
|
return make_response(jsonify({"success": True}), 200)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
|
|
@agents_folders_ns.route("/move_agent")
|
|
class MoveAgentToFolder(Resource):
|
|
@api.doc(description="Move an agent to a folder or remove from folder")
|
|
@api.expect(
|
|
api.model(
|
|
"MoveAgent",
|
|
{
|
|
"agent_id": fields.String(required=True, description="Agent ID to move"),
|
|
"folder_id": fields.String(required=False, description="Target folder ID (null to remove from folder)"),
|
|
},
|
|
)
|
|
)
|
|
def post(self):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
data = request.get_json()
|
|
if not data or not data.get("agent_id"):
|
|
return make_response(jsonify({"success": False, "message": "Agent ID is required"}), 400)
|
|
|
|
agent_id = data["agent_id"]
|
|
folder_id = data.get("folder_id")
|
|
|
|
try:
|
|
agent = agents_collection.find_one({"_id": ObjectId(agent_id), "user": user})
|
|
if not agent:
|
|
return make_response(jsonify({"success": False, "message": "Agent not found"}), 404)
|
|
|
|
if folder_id:
|
|
folder = agent_folders_collection.find_one({"_id": ObjectId(folder_id), "user": user})
|
|
if not folder:
|
|
return make_response(jsonify({"success": False, "message": "Folder not found"}), 404)
|
|
agents_collection.update_one(
|
|
{"_id": ObjectId(agent_id)}, {"$set": {"folder_id": folder_id}}
|
|
)
|
|
else:
|
|
agents_collection.update_one(
|
|
{"_id": ObjectId(agent_id)}, {"$unset": {"folder_id": ""}}
|
|
)
|
|
|
|
return make_response(jsonify({"success": True}), 200)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|
|
|
|
|
|
@agents_folders_ns.route("/bulk_move")
|
|
class BulkMoveAgents(Resource):
|
|
@api.doc(description="Move multiple agents to a folder")
|
|
@api.expect(
|
|
api.model(
|
|
"BulkMoveAgents",
|
|
{
|
|
"agent_ids": fields.List(fields.String, required=True, description="List of agent IDs"),
|
|
"folder_id": fields.String(required=False, description="Target folder ID"),
|
|
},
|
|
)
|
|
)
|
|
def post(self):
|
|
decoded_token = request.decoded_token
|
|
if not decoded_token:
|
|
return make_response(jsonify({"success": False}), 401)
|
|
user = decoded_token.get("sub")
|
|
data = request.get_json()
|
|
if not data or not data.get("agent_ids"):
|
|
return make_response(jsonify({"success": False, "message": "Agent IDs are required"}), 400)
|
|
|
|
agent_ids = data["agent_ids"]
|
|
folder_id = data.get("folder_id")
|
|
|
|
try:
|
|
if folder_id:
|
|
folder = agent_folders_collection.find_one({"_id": ObjectId(folder_id), "user": user})
|
|
if not folder:
|
|
return make_response(jsonify({"success": False, "message": "Folder not found"}), 404)
|
|
|
|
object_ids = [ObjectId(aid) for aid in agent_ids]
|
|
if folder_id:
|
|
agents_collection.update_many(
|
|
{"_id": {"$in": object_ids}, "user": user},
|
|
{"$set": {"folder_id": folder_id}},
|
|
)
|
|
else:
|
|
agents_collection.update_many(
|
|
{"_id": {"$in": object_ids}, "user": user},
|
|
{"$unset": {"folder_id": ""}},
|
|
)
|
|
return make_response(jsonify({"success": True}), 200)
|
|
except Exception as e:
|
|
return make_response(jsonify({"success": False, "message": str(e)}), 400)
|