Merge branch 'main' into refactor/llm-handler

This commit is contained in:
Siddhant Rai
2025-06-06 17:36:31 +05:30
committed by GitHub
28 changed files with 547 additions and 489 deletions

View File

@@ -15,6 +15,7 @@ from flask_restx import fields, inputs, Namespace, Resource
from werkzeug.utils import secure_filename
from application.agents.tools.tool_manager import ToolManager
from pymongo import ReturnDocument
from application.api.user.tasks import (
ingest,
@@ -50,6 +51,7 @@ agents_collection.create_index(
name="shared_index",
background=True,
)
users_collection.create_index("user_id", unique=True)
user = Blueprint("user", __name__)
user_ns = Namespace("user", description="User related operations", path="/")
@@ -85,30 +87,44 @@ def generate_date_range(start_date, end_date):
def ensure_user_doc(user_id):
default_prefs = {
"pinned": [],
"shared_with_me": [],
}
user_doc = users_collection.find_one_and_update(
{"user_id": user_id},
{"$setOnInsert": {"agent_preferences": default_prefs}},
upsert=True,
return_document=ReturnDocument.AFTER,
)
prefs = user_doc.get("agent_preferences", {})
updates = {}
if "pinned" not in prefs:
updates["agent_preferences.pinned"] = []
if "shared_with_me" not in prefs:
updates["agent_preferences.shared_with_me"] = []
if updates:
users_collection.update_one({"user_id": user_id}, {"$set": updates})
user_doc = users_collection.find_one({"user_id": user_id})
if not user_doc:
user_doc = {
"user_id": user_id,
"agent_preferences": {"pinned": [], "hidden_shared": []},
}
users_collection.insert_one(user_doc)
return user_doc
updated = False
preferences = user_doc.get("agent_preferences", {})
if "pinned" not in preferences:
preferences["pinned"] = []
updated = True
if "hidden_shared" not in preferences:
preferences["hidden_shared"] = []
updated = True
if updated:
users_collection.update_one(
{"user_id": user_id}, {"$set": {"agent_preferences": preferences}}
def resolve_tool_details(tool_ids):
tools = user_tools_collection.find(
{"_id": {"$in": [ObjectId(tid) for tid in tool_ids]}}
)
user_doc["agent_preferences"] = preferences
return user_doc
return [
{
"id": str(tool["_id"]),
"name": tool.get("name", ""),
"display_name": tool.get("displayName", tool.get("name", "")),
}
for tool in tools
]
def get_vector_store(source_id):
@@ -1057,6 +1073,7 @@ class GetAgent(Resource):
"retriever": agent.get("retriever", ""),
"prompt_id": agent.get("prompt_id", ""),
"tools": agent.get("tools", []),
"tool_details": resolve_tool_details(agent.get("tools", [])),
"agent_type": agent.get("agent_type", ""),
"status": agent.get("status", ""),
"created_at": agent.get("createdAt", ""),
@@ -1106,6 +1123,7 @@ class GetAgents(Resource):
"retriever": agent.get("retriever", ""),
"prompt_id": agent.get("prompt_id", ""),
"tools": agent.get("tools", []),
"tool_details": resolve_tool_details(agent.get("tools", [])),
"agent_type": agent.get("agent_type", ""),
"status": agent.get("status", ""),
"created_at": agent.get("createdAt", ""),
@@ -1464,26 +1482,25 @@ class PinnedAgents(Resource):
decoded_token = request.decoded_token
if not decoded_token:
return make_response(jsonify({"success": False}), 401)
user_id = decoded_token.get("sub")
try:
user_doc = ensure_user_doc(user_id)
pinned_ids = user_doc.get("agent_preferences", {}).get("pinned", [])
hidden_ids = set(
user_doc.get("agent_preferences", {}).get("hidden_shared", [])
)
if not pinned_ids:
return make_response(jsonify([]), 200)
pinned_object_ids = [ObjectId(agent_id) for agent_id in pinned_ids]
pinned_agents_cursor = agents_collection.find(
{"_id": {"$in": pinned_object_ids}}
)
pinned_agents = list(pinned_agents_cursor)
existing_ids = {str(agent["_id"]) for agent in pinned_agents}
existing_agents = pinned_agents
existing_ids = {str(agent["_id"]) for agent in existing_agents}
# Clean up any stale pinned IDs
stale_ids = [
agent_id for agent_id in pinned_ids if agent_id not in existing_ids
]
@@ -1500,13 +1517,17 @@ class PinnedAgents(Resource):
"description": agent.get("description", ""),
"source": (
str(db.dereference(agent["source"])["_id"])
if "source" in agent and isinstance(agent["source"], DBRef)
if "source" in agent
and agent["source"]
and isinstance(agent["source"], DBRef)
and db.dereference(agent["source"]) is not None
else ""
),
"chunks": agent.get("chunks", ""),
"retriever": agent.get("retriever", ""),
"prompt_id": agent.get("prompt_id", ""),
"tools": agent.get("tools", []),
"tool_details": resolve_tool_details(agent.get("tools", [])),
"agent_type": agent.get("agent_type", ""),
"status": agent.get("status", ""),
"created_at": agent.get("createdAt", ""),
@@ -1520,12 +1541,13 @@ class PinnedAgents(Resource):
"pinned": True,
}
for agent in pinned_agents
if ("source" in agent or "retriever" in agent)
and str(agent["_id"]) not in hidden_ids
if "source" in agent or "retriever" in agent
]
except Exception as err:
current_app.logger.error(f"Error retrieving pinned agents: {err}")
return make_response(jsonify({"success": False}), 400)
return make_response(jsonify(list_pinned_agents), 200)
@@ -1572,11 +1594,11 @@ class PinAgent(Resource):
return make_response(jsonify({"success": True, "action": action}), 200)
@user_ns.route("/api/hide_shared_agent")
class HideSharedAgent(Resource):
@user_ns.route("/api/remove_shared_agent")
class RemoveSharedAgent(Resource):
@api.doc(
params={"id": "ID of the shared agent"},
description="Hide or unhide a shared agent for the current user",
description="Remove a shared agent from the current user's shared list",
)
def delete(self):
decoded_token = request.decoded_token
@@ -1589,6 +1611,7 @@ class HideSharedAgent(Resource):
return make_response(
jsonify({"success": False, "message": "ID is required"}), 400
)
try:
agent = agents_collection.find_one(
{"_id": ObjectId(agent_id), "shared_publicly": True}
@@ -1598,27 +1621,25 @@ class HideSharedAgent(Resource):
jsonify({"success": False, "message": "Shared agent not found"}),
404,
)
user_doc = ensure_user_doc(user_id)
hidden_list = user_doc.get("agent_preferences", {}).get("hidden_shared", [])
if agent_id in hidden_list:
ensure_user_doc(user_id)
users_collection.update_one(
{"user_id": user_id},
{"$pull": {"agent_preferences.hidden_shared": agent_id}},
{
"$pull": {
"agent_preferences.shared_with_me": agent_id,
"agent_preferences.pinned": agent_id,
}
},
)
action = "unhidden"
else:
users_collection.update_one(
{"user_id": user_id},
{"$addToSet": {"agent_preferences.hidden_shared": agent_id}},
)
action = "hidden"
return make_response(jsonify({"success": True, "action": "removed"}), 200)
except Exception as err:
current_app.logger.error(f"Error hiding/unhiding shared agent: {err}")
current_app.logger.error(f"Error removing shared agent: {err}")
return make_response(
jsonify({"success": False, "message": "Server error"}), 500
)
return make_response(jsonify({"success": True, "action": action}), 200)
@user_ns.route("/api/shared_agent")
@@ -1636,23 +1657,27 @@ class SharedAgent(Resource):
return make_response(
jsonify({"success": False, "message": "Token or ID is required"}), 400
)
try:
query = {}
query["shared_publicly"] = True
query["shared_token"] = shared_token
try:
query = {
"shared_publicly": True,
"shared_token": shared_token,
}
shared_agent = agents_collection.find_one(query)
if not shared_agent:
return make_response(
jsonify({"success": False, "message": "Shared agent not found"}),
404,
)
agent_id = str(shared_agent["_id"])
data = {
"id": str(shared_agent["_id"]),
"id": agent_id,
"user": shared_agent.get("user", ""),
"name": shared_agent.get("name", ""),
"description": shared_agent.get("description", ""),
"tools": shared_agent.get("tools", []),
"tool_details": resolve_tool_details(shared_agent.get("tools", [])),
"agent_type": shared_agent.get("agent_type", ""),
"status": shared_agent.get("status", ""),
"created_at": shared_agent.get("createdAt", ""),
@@ -1669,15 +1694,29 @@ class SharedAgent(Resource):
if tool_data:
enriched_tools.append(tool_data.get("name", ""))
data["tools"] = enriched_tools
decoded_token = getattr(request, "decoded_token", None)
if decoded_token:
user_id = decoded_token.get("sub")
owner_id = shared_agent.get("user")
if user_id != owner_id:
ensure_user_doc(user_id)
users_collection.update_one(
{"user_id": user_id},
{"$addToSet": {"agent_preferences.shared_with_me": agent_id}},
)
return make_response(jsonify(data), 200)
except Exception as err:
current_app.logger.error(f"Error retrieving shared agent: {err}")
return make_response(jsonify({"success": False}), 400)
return make_response(jsonify(data), 200)
@user_ns.route("/api/shared_agents")
class SharedAgents(Resource):
@api.doc(description="Get shared agents")
@api.doc(description="Get shared agents explicitly shared with the user")
def get(self):
try:
decoded_token = request.decoded_token
@@ -1686,29 +1725,25 @@ class SharedAgents(Resource):
user_id = decoded_token.get("sub")
user_doc = ensure_user_doc(user_id)
pinned_ids = set(user_doc.get("agent_preferences", {}).get("pinned", []))
hidden_ids = user_doc.get("agent_preferences", {}).get("hidden_shared", [])
hidden_object_ids = [ObjectId(id) for id in hidden_ids]
shared_with_ids = user_doc.get("agent_preferences", {}).get(
"shared_with_me", []
)
shared_object_ids = [ObjectId(id) for id in shared_with_ids]
shared_agents_cursor = agents_collection.find(
{"shared_publicly": True, "user": {"$ne": user_id}}
{"_id": {"$in": shared_object_ids}, "shared_publicly": True}
)
shared_agents = list(shared_agents_cursor)
shared_ids_set = {agent["_id"] for agent in shared_agents}
hidden_ids_set = set(hidden_object_ids)
stale_hidden_ids = [
str(id) for id in hidden_ids_set if id not in shared_ids_set
]
if stale_hidden_ids:
found_ids_set = {str(agent["_id"]) for agent in shared_agents}
stale_ids = [id for id in shared_with_ids if id not in found_ids_set]
if stale_ids:
users_collection.update_one(
{"user_id": user_id},
{"$pullAll": {"agent_preferences.hidden_shared": stale_hidden_ids}},
{"$pullAll": {"agent_preferences.shared_with_me": stale_ids}},
)
visible_shared_agents = [
agent for agent in shared_agents if agent["_id"] not in hidden_ids_set
]
pinned_ids = set(user_doc.get("agent_preferences", {}).get("pinned", []))
list_shared_agents = [
{
@@ -1716,6 +1751,7 @@ class SharedAgents(Resource):
"name": agent.get("name", ""),
"description": agent.get("description", ""),
"tools": agent.get("tools", []),
"tool_details": resolve_tool_details(agent.get("tools", [])),
"agent_type": agent.get("agent_type", ""),
"status": agent.get("status", ""),
"created_at": agent.get("createdAt", ""),
@@ -1725,10 +1761,11 @@ class SharedAgents(Resource):
"shared_token": agent.get("shared_token", ""),
"shared_metadata": agent.get("shared_metadata", {}),
}
for agent in visible_shared_agents
for agent in shared_agents
]
return make_response(jsonify(list_shared_agents), 200)
except Exception as err:
current_app.logger.error(f"Error retrieving shared agents: {err}")
return make_response(jsonify({"success": False}), 400)

View File

@@ -46,7 +46,7 @@ pandas==2.2.3
openpyxl==3.1.5
pathable==0.4.4
pillow==11.1.0
portalocker==3.1.1
portalocker>=2.7.0,<3.0.0
prance==23.6.21.0
prompt-toolkit==3.0.51
protobuf==5.29.3
@@ -62,7 +62,7 @@ python-dotenv==1.0.1
python-jose==3.4.0
python-pptx==1.0.2
redis==5.2.1
referencing==0.36.2
referencing>=0.28.0,<0.31.0
regex==2024.11.6
requests==2.32.3
retry==0.9.2

View File

@@ -44,8 +44,8 @@ class ClassicRAG(BaseRetriever):
user_api_key=self.user_api_key,
decoded_token=decoded_token,
)
self.question = self._rephrase_query()
self.vectorstore = source["active_docs"] if "active_docs" in source else None
self.question = self._rephrase_query()
self.decoded_token = decoded_token
def _rephrase_query(self):
@@ -53,6 +53,8 @@ class ClassicRAG(BaseRetriever):
not self.original_question
or not self.chat_history
or self.chat_history == []
or self.chunks == 0
or self.vectorstore is None
):
return self.original_question
@@ -77,7 +79,7 @@ class ClassicRAG(BaseRetriever):
return self.original_question
def _get_data(self):
if self.chunks == 0:
if self.chunks == 0 or self.vectorstore is None:
docs = []
else:
docsearch = VectorCreator.create_vectorstore(

View File

@@ -3,18 +3,18 @@ from application.retriever.duckduck_search import DuckDuckSearch
from application.retriever.brave_search import BraveRetSearch
class RetrieverCreator:
retrievers = {
'classic': ClassicRAG,
'duckduck_search': DuckDuckSearch,
'brave_search': BraveRetSearch,
'default': ClassicRAG
"classic": ClassicRAG,
"duckduck_search": DuckDuckSearch,
"brave_search": BraveRetSearch,
"default": ClassicRAG,
}
@classmethod
def create_retriever(cls, type, *args, **kwargs):
retiever_class = cls.retrievers.get(type.lower())
retriever_type = (type or "default").lower()
retiever_class = cls.retrievers.get(retriever_type)
if not retiever_class:
raise ValueError(f"No retievers class found for type {type}")
return retiever_class(*args, **kwargs)

View File

@@ -33,8 +33,12 @@ class FaissStore(BaseVectorStore):
faiss_path = f"{self.path}/index.faiss"
pkl_path = f"{self.path}/index.pkl"
if not self.storage.file_exists(faiss_path) or not self.storage.file_exists(pkl_path):
raise FileNotFoundError(f"Index files not found in storage at {self.path}")
if not self.storage.file_exists(
faiss_path
) or not self.storage.file_exists(pkl_path):
raise FileNotFoundError(
f"Index files not found in storage at {self.path}"
)
faiss_file = self.storage.get_file(faiss_path)
pkl_file = self.storage.get_file(pkl_path)
@@ -42,10 +46,10 @@ class FaissStore(BaseVectorStore):
local_faiss_path = os.path.join(temp_dir, "index.faiss")
local_pkl_path = os.path.join(temp_dir, "index.pkl")
with open(local_faiss_path, 'wb') as f:
with open(local_faiss_path, "wb") as f:
f.write(faiss_file.read())
with open(local_pkl_path, 'wb') as f:
with open(local_pkl_path, "wb") as f:
f.write(pkl_file.read())
self.docsearch = FAISS.load_local(

View File

@@ -1,99 +0,0 @@
//TODO - Add hyperlinks to text
//TODO - Styling
import DocsGPT3 from './assets/cute_docsgpt3.svg';
export default function About() {
return (
<div className="mx-5 grid min-h-screen md:mx-36">
<article className="place-items-left mx-auto my-auto flex w-full max-w-6xl flex-col gap-4 rounded-3xl bg-gray-100 p-6 text-jet dark:bg-gun-metal dark:text-bright-gray lg:p-6 xl:p-10">
<div className="flex items-center">
<p className="mr-2 text-3xl">About DocsGPT</p>
<img className="h14 mb-2" src={DocsGPT3} alt="DocsGPT" />
</div>
<p className="mt-4">
Find the information in your documentation through AI-powered
<a
className="text-blue-500"
href="https://github.com/arc53/DocsGPT"
target="_blank"
rel="noreferrer"
>
{' '}
open-source{' '}
</a>
chatbot. Powered by GPT-3, Faiss and LangChain.
</p>
<div>
<p>
If you want to add your own documentation, please follow the
instruction below:
</p>
<p className="ml-2 mt-4">
1. Navigate to{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
{' '}
/application
</span>{' '}
folder
</p>
<p className="ml-2 mt-4">
2. Install dependencies from{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
pip install -r requirements.txt
</span>
</p>
<p className="ml-2 mt-4">
3. Prepare a{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
file. Copy{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
.env_sample
</span>{' '}
and create{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
with your OpenAI API token
</p>
<p className="ml-2 mt-4">
4. Run the app with{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
python app.py
</span>
</p>
</div>
<p>
Currently It uses{' '}
<span className="font-medium text-blue-950">DocsGPT</span>{' '}
documentation, so it will respond to information relevant to{' '}
<span className="font-medium text-blue-950">DocsGPT</span>. If you
want to train it on different documentation - please follow
<a
className="text-blue-500"
href="https://github.com/arc53/DocsGPT/wiki/How-to-train-on-other-documentation"
target="_blank"
rel="noreferrer"
>
{' '}
this guide
</a>
.
</p>
<p className="mt-4 text-left">
If you want to launch it on your own server - follow
<a
className="text-blue-500"
href="https://github.com/arc53/DocsGPT/wiki/Hosting-the-app"
target="_blank"
rel="noreferrer"
>
{' '}
this guide
</a>
.
</p>
</article>
</div>
);
}

View File

@@ -3,7 +3,9 @@ import './locale/i18n';
import { useState } from 'react';
import { Outlet, Route, Routes } from 'react-router-dom';
import About from './About';
import Agents from './agents';
import SharedAgentGate from './agents/SharedAgentGate';
import ActionButtons from './components/ActionButtons';
import Spinner from './components/Spinner';
import Conversation from './conversation/Conversation';
import { SharedConversation } from './conversation/SharedConversation';
@@ -12,8 +14,6 @@ import useTokenAuth from './hooks/useTokenAuth';
import Navigation from './Navigation';
import PageNotFound from './PageNotFound';
import Setting from './settings';
import Agents from './agents';
import ActionButtons from './components/ActionButtons';
function AuthWrapper({ children }: { children: React.ReactNode }) {
const { isAuthLoading } = useTokenAuth();
@@ -29,8 +29,8 @@ function AuthWrapper({ children }: { children: React.ReactNode }) {
}
function MainLayout() {
const { isMobile } = useMediaQuery();
const [navOpen, setNavOpen] = useState(!isMobile);
const { isMobile, isTablet } = useMediaQuery();
const [navOpen, setNavOpen] = useState(!(isMobile || isTablet));
return (
<div className="relative h-screen overflow-hidden dark:bg-raisin-black">
@@ -38,7 +38,7 @@ function MainLayout() {
<ActionButtons showNewChat={true} showShare={true} />
<div
className={`h-[calc(100dvh-64px)] overflow-auto lg:h-screen ${
!isMobile
!(isMobile || isTablet)
? `ml-0 ${!navOpen ? 'lg:mx-auto' : 'lg:ml-72'}`
: 'ml-0 lg:ml-16'
}`}
@@ -64,11 +64,11 @@ export default function App() {
}
>
<Route index element={<Conversation />} />
<Route path="/about" element={<About />} />
<Route path="/settings/*" element={<Setting />} />
<Route path="/agents/*" element={<Agents />} />
</Route>
<Route path="/share/:identifier" element={<SharedConversation />} />
<Route path="/shared/agent/:agentId" element={<SharedAgentGate />} />
<Route path="/*" element={<PageNotFound />} />
</Routes>
</div>

View File

@@ -72,7 +72,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const sharedAgents = useSelector(selectSharedAgents);
const selectedAgent = useSelector(selectSelectedAgent);
const { isMobile } = useMediaQuery();
const { isMobile, isTablet } = useMediaQuery();
const [isDarkTheme] = useDarkTheme();
const { showTokenModal, handleTokenSubmit } = useTokenAuth();
@@ -163,7 +163,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const handleAgentClick = (agent: Agent) => {
resetConversation();
dispatch(setSelectedAgent(agent));
if (isMobile) setNavOpen(!navOpen);
if (isMobile || isTablet) setNavOpen(!navOpen);
navigate('/');
};
@@ -267,8 +267,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
}
useEffect(() => {
setNavOpen(!isMobile);
}, [isMobile]);
setNavOpen(!(isMobile || isTablet));
}, [isMobile, isTablet]);
useDefaultDocument();
return (
@@ -348,7 +348,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
<NavLink
to={'/'}
onClick={() => {
if (isMobile) {
if (isMobile || isTablet) {
setNavOpen(!navOpen);
}
resetConversation();
@@ -411,7 +411,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
</p>
</div>
<div
className={`${isMobile ? 'flex' : 'invisible flex group-hover:visible'} items-center px-3`}
className={`${isMobile || isTablet ? 'flex' : 'invisible flex group-hover:visible'} items-center px-3`}
>
<button
className="rounded-full hover:opacity-75"
@@ -433,6 +433,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
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={() => {
dispatch(setSelectedAgent(null));
if (isMobile || isTablet) {
setNavOpen(false);
}
navigate('/agents');
}}
>
@@ -444,7 +447,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
/>
</div>
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap text-sm leading-6 text-eerie-black dark:text-bright-gray">
Manage Agents
{t('manageAgents')}
</p>
</div>
</div>
@@ -452,7 +455,13 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
) : (
<div
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={() => {
if (isMobile || isTablet) {
setNavOpen(false);
}
dispatch(setSelectedAgent(null));
navigate('/agents');
}}
>
<div className="flex w-6 justify-center">
<img
@@ -462,7 +471,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
/>
</div>
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap text-sm leading-6 text-eerie-black dark:text-bright-gray">
Manage Agents
{t('manageAgents')}
</p>
</div>
)}
@@ -498,8 +507,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
<div className="flex flex-col gap-2 border-b-[1px] py-2 dark:border-b-purple-taupe">
<NavLink
onClick={() => {
if (isMobile) {
setNavOpen(!navOpen);
if (isMobile || isTablet) {
setNavOpen(false);
}
resetConversation();
}}

View File

@@ -21,6 +21,7 @@ import {
import { useDarkTheme } from '../hooks';
import { selectToken, setSelectedAgent } from '../preferences/preferenceSlice';
import { AppDispatch } from '../store';
import SharedAgentCard from './SharedAgentCard';
import { Agent } from './types';
export default function SharedAgent() {
@@ -193,65 +194,3 @@ export default function SharedAgent() {
</div>
);
}
function SharedAgentCard({ agent }: { agent: Agent }) {
return (
<div className="flex w-full max-w-[720px] flex-col rounded-3xl border border-dark-gray p-6 shadow-sm dark:border-grey sm:w-fit sm:min-w-[480px]">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center overflow-hidden rounded-full p-1">
<img src={Robot} className="h-full w-full object-contain" />
</div>
<div className="flex max-h-[92px] w-[80%] flex-col gap-px">
<h2 className="text-base font-semibold text-[#212121] dark:text-[#E0E0E0] sm:text-lg">
{agent.name}
</h2>
<p className="overflow-y-auto text-wrap break-all text-xs text-[#71717A] dark:text-[#949494] sm:text-sm">
{agent.description}
</p>
</div>
</div>
<div className="mt-4 flex items-center gap-8">
{agent.shared_metadata?.shared_by && (
<p className="text-xs font-light text-[#212121] dark:text-[#E0E0E0] sm:text-sm">
by {agent.shared_metadata.shared_by}
</p>
)}
{agent.shared_metadata?.shared_at && (
<p className="text-xs font-light text-[#71717A] dark:text-[#949494] sm:text-sm">
Shared on{' '}
{new Date(agent.shared_metadata.shared_at).toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true,
})}
</p>
)}
</div>
{agent.tools.length > 0 && (
<div className="mt-8">
<p className="text-sm font-semibold text-[#212121] dark:text-[#E0E0E0] sm:text-base">
Connected Tools
</p>
<div className="mt-2 flex flex-wrap gap-2">
{agent.tools.map((tool, index) => (
<span
key={index}
className="flex items-center gap-1 rounded-full bg-bright-gray px-3 py-1 text-xs font-light text-[#212121] dark:bg-dark-charcoal dark:text-[#E0E0E0]"
>
<img
src={`/toolIcons/tool_${tool}.svg`}
alt={`${tool} icon`}
className="h-3 w-3"
/>{' '}
{tool}
</span>
))}
</div>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,69 @@
import Robot from '../assets/robot.svg';
import { Agent } from './types';
export default function SharedAgentCard({ agent }: { agent: Agent }) {
return (
<div className="flex w-full max-w-[720px] flex-col rounded-3xl border border-dark-gray p-6 shadow-sm dark:border-grey sm:w-fit sm:min-w-[480px]">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center overflow-hidden rounded-full p-1">
<img src={Robot} className="h-full w-full object-contain" />
</div>
<div className="flex max-h-[92px] w-[80%] flex-col gap-px">
<h2 className="text-base font-semibold text-[#212121] dark:text-[#E0E0E0] sm:text-lg">
{agent.name}
</h2>
<p className="overflow-y-auto text-wrap break-all text-xs text-[#71717A] dark:text-[#949494] sm:text-sm">
{agent.description}
</p>
</div>
</div>
{agent.shared_metadata && (
<div className="mt-4 flex items-center gap-8">
{agent.shared_metadata?.shared_by && (
<p className="text-xs font-light text-[#212121] dark:text-[#E0E0E0] sm:text-sm">
by {agent.shared_metadata.shared_by}
</p>
)}
{agent.shared_metadata?.shared_at && (
<p className="text-xs font-light text-[#71717A] dark:text-[#949494] sm:text-sm">
Shared on{' '}
{new Date(agent.shared_metadata.shared_at).toLocaleString(
'en-US',
{
month: 'long',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
)}
</p>
)}
</div>
)}
{agent.tool_details && agent.tool_details.length > 0 && (
<div className="mt-8">
<p className="text-sm font-semibold text-[#212121] dark:text-[#E0E0E0] sm:text-base">
Connected Tools
</p>
<div className="mt-2 flex flex-wrap gap-2">
{agent.tool_details.map((tool, index) => (
<span
key={index}
className="flex items-center gap-1 rounded-full bg-bright-gray px-3 py-1 text-xs font-light text-[#212121] dark:bg-dark-charcoal dark:text-[#E0E0E0]"
>
<img
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.name} icon`}
className="h-3 w-3"
/>{' '}
{tool.name}
</span>
))}
</div>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,7 @@
import { Navigate, useParams } from 'react-router-dom';
export default function SharedAgentGate() {
const { agentId } = useParams();
return <Navigate to={`/agents/shared/${agentId}`} replace />;
}

View File

@@ -286,7 +286,10 @@ function AgentCard({
const handleHideSharedAgent = async () => {
try {
const response = await userService.hideSharedAgent(agent.id ?? '', token);
const response = await userService.removeSharedAgent(
agent.id ?? '',
token,
);
if (!response.ok) throw new Error('Failed to hide shared agent');
const updatedAgents = agents.filter(
(prevAgent) => prevAgent.id !== agent.id,

View File

@@ -1,3 +1,9 @@
export type ToolSummary = {
id: string;
name: string;
display_name: string;
};
export type Agent = {
id?: string;
name: string;
@@ -8,6 +14,7 @@ export type Agent = {
retriever: string;
prompt_id: string;
tools: string[];
tool_details?: ToolSummary[];
agent_type: string;
status: string;
key?: string;

View File

@@ -18,7 +18,7 @@ const endpoints = {
SHARED_AGENT: (id: string) => `/api/shared_agent?token=${id}`,
SHARED_AGENTS: '/api/shared_agents',
SHARE_AGENT: `/api/share_agent`,
HIDE_SHARED_AGENT: (id: string) => `/api/hide_shared_agent?id=${id}`,
REMOVE_SHARED_AGENT: (id: string) => `/api/remove_shared_agent?id=${id}`,
AGENT_WEBHOOK: (id: string) => `/api/agent_webhook?id=${id}`,
PROMPTS: '/api/get_prompts',
CREATE_PROMPT: '/api/create_prompt',

View File

@@ -41,8 +41,8 @@ const userService = {
apiClient.get(endpoints.USER.SHARED_AGENTS, token),
shareAgent: (data: any, token: string | null): Promise<any> =>
apiClient.put(endpoints.USER.SHARE_AGENT, data, token),
hideSharedAgent: (id: string, token: string | null): Promise<any> =>
apiClient.delete(endpoints.USER.HIDE_SHARED_AGENT(id), token),
removeSharedAgent: (id: string, token: string | null): Promise<any> =>
apiClient.delete(endpoints.USER.REMOVE_SHARED_AGENT(id), token),
getAgentWebhook: (id: string, token: string | null): Promise<any> =>
apiClient.get(endpoints.USER.AGENT_WEBHOOK(id), token),
getPrompts: (token: string | null): Promise<any> =>

View File

@@ -207,7 +207,7 @@ export default function SourcesPopup({
className="inline-flex items-center gap-2 text-base font-medium text-violets-are-blue"
onClick={onClose}
>
Go to Documents
{t('settings.documents.goToDocuments')}
<img src={RedirectIcon} alt="Redirect" className="h-3 w-3" />
</a>
</div>
@@ -217,7 +217,7 @@ export default function SourcesPopup({
onClick={handleUploadClick}
className="w-auto rounded-full border border-violets-are-blue px-4 py-2 text-[14px] font-medium text-violets-are-blue transition-colors duration-200 hover:bg-violets-are-blue hover:text-white"
>
Upload new
{t('settings.documents.uploadNew')}
</button>
</div>
</div>

View File

@@ -3,6 +3,7 @@ import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import SharedAgentCard from '../agents/SharedAgentCard';
import DragFileUpload from '../assets/DragFileUpload.svg';
import MessageInput from '../components/MessageInput';
import { useMediaQuery } from '../hooks';
@@ -193,6 +194,14 @@ export default function Conversation() {
handleFeedback={handleFeedback}
queries={queries}
status={status}
showHeroOnEmpty={selectedAgent ? false : true}
headerContent={
selectedAgent ? (
<div className="flex w-full items-center justify-center py-4">
<SharedAgentCard agent={selectedAgent} />
</div>
) : undefined
}
/>
<div className="z-3 flex h-auto w-full max-w-[1300px] flex-col items-end self-center rounded-2xl bg-opacity-0 py-1 md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">

View File

@@ -35,8 +35,8 @@ export function useOutsideAlerter<T extends HTMLElement>(
export function useMediaQuery() {
const mobileQuery = '(max-width: 768px)';
const tabletQuery = '(max-width: 1024px)'; // Tablet breakpoint at 1024px
const desktopQuery = '(min-width: 1025px)'; // Desktop starts after tablet
const tabletQuery = '(max-width: 1023px)';
const desktopQuery = '(min-width: 1024px)';
const [isMobile, setIsMobile] = useState(false);
const [isTablet, setIsTablet] = useState(false);
const [isDesktop, setIsDesktop] = useState(false);

View File

@@ -77,7 +77,9 @@
"backToAll": "Back to all documents",
"chunks": "Chunks",
"noChunks": "No chunks found",
"noChunksAlt": "No chunks found"
"noChunksAlt": "No chunks found",
"goToDocuments": "Go to Documents",
"uploadNew": "Upload new"
},
"apiKeys": {
"label": "Chatbots",
@@ -138,7 +140,7 @@
"addAction": "Add action",
"noActionsFound": "No actions found",
"url": "URL",
"urlPlaceholder": "Enter url",
"urlPlaceholder": "Enter URL",
"method": "Method",
"description": "Description",
"descriptionPlaceholder": "Enter description",
@@ -146,15 +148,20 @@
"queryParameters": "Query Parameters",
"body": "Body",
"deleteActionWarning": "Are you sure you want to delete the action \"{{name}}\"?",
"backToTools": "Back to Tools",
"backToAllTools": "Back to all tools",
"save": "Save",
"name": "Name",
"type": "Type",
"fieldName": "Field Name",
"fieldType": "Field Type",
"filledByLLM": "Filled by LLM",
"fieldDescription": "Field description",
"value": "Value",
"addProperty": "Add property",
"propertyName": "Property name",
"noProperties": "No properties"
"propertyName": "New property key",
"add": "Add",
"cancel": "Cancel",
"addNew": "Add New",
"name": "Name",
"type": "Type"
}
},
"modals": {
@@ -239,6 +246,18 @@
"promptText": "Prompt Text",
"save": "Save",
"nameExists": "Name already exists"
},
"chunk": {
"add": "Add Chunk",
"edit": "Edit Chunk",
"title": "Title",
"enterTitle": "Enter title",
"bodyText": "Body text",
"promptText": "Prompt Text",
"update": "Update",
"close": "Close",
"delete": "Delete",
"deleteConfirmation": "Are you sure you want to delete this chunk?"
}
},
"sharedConv": {

View File

@@ -11,6 +11,7 @@
"help": "Asistencia",
"emailUs": "Envíanos un correo",
"documentation": "Documentación",
"manageAgents": "Administrar Agentes",
"demo": [
{
"header": "Aprende sobre DocsGPT",
@@ -48,7 +49,8 @@
"medium": "Medio",
"high": "Alto",
"unlimited": "Ilimitado",
"default": "Predeterminado"
"default": "Predeterminado",
"addNew": "Añadir Nuevo"
},
"documents": {
"title": "Esta tabla contiene todos los documentos que están disponibles para ti y los que has subido",
@@ -75,7 +77,9 @@
"backToAll": "Volver a todos los documentos",
"chunks": "Fragmentos",
"noChunks": "No se encontraron fragmentos",
"noChunksAlt": "No se encontraron fragmentos"
"noChunksAlt": "No se encontraron fragmentos",
"goToDocuments": "Ir a Documentos",
"uploadNew": "Subir nuevo"
},
"apiKeys": {
"label": "Chatbots",
@@ -144,15 +148,20 @@
"queryParameters": "Parámetros de Consulta",
"body": "Cuerpo",
"deleteActionWarning": "¿Estás seguro de que deseas eliminar la acción \"{{name}}\"?",
"backToTools": "Volver a Herramientas",
"save": "Guardar",
"name": "Nombre",
"type": "Tipo",
"filledByLLM": "Completado por LLM",
"value": "Valor",
"addProperty": "Agregar propiedad",
"propertyName": "Nombre de propiedad",
"noProperties": "Sin propiedades"
"propertyName": "Nueva clave de propiedad",
"backToAllTools": "Volver a todas las herramientas",
"fieldName": "Nombre del campo",
"fieldType": "Tipo de campo",
"fieldDescription": "Descripción del campo",
"add": "Añadir",
"cancel": "Cancelar",
"addNew": "Añadir Nuevo"
}
},
"modals": {
@@ -237,6 +246,18 @@
"promptText": "Texto del Prompt",
"save": "Guardar",
"nameExists": "El nombre ya existe"
},
"chunk": {
"add": "Agregar Fragmento",
"edit": "Editar Fragmento",
"title": "Título",
"enterTitle": "Ingresar título",
"bodyText": "Texto del cuerpo",
"promptText": "Texto del prompt",
"update": "Actualizar",
"close": "Cerrar",
"delete": "Eliminar",
"deleteConfirmation": "¿Estás seguro de que deseas eliminar este fragmento?"
}
},
"sharedConv": {
@@ -270,9 +291,9 @@
},
"sources": {
"title": "Fuentes",
"text": "Texto fuente",
"link": "Enlace fuente",
"view_more": "Ver {{count}} más fuentes"
"view_more": "Ver {{count}} más fuentes",
"text": "Elegir tus fuentes"
},
"attachments": {
"attach": "Adjuntar",

View File

@@ -11,6 +11,7 @@
"help": "ヘルプ",
"emailUs": "メールを送る",
"documentation": "ドキュメント",
"manageAgents": "エージェント管理",
"demo": [
{
"header": "DocsGPTについて学ぶ",
@@ -76,7 +77,9 @@
"backToAll": "すべてのドキュメントに戻る",
"chunks": "チャンク",
"noChunks": "チャンクが見つかりません",
"noChunksAlt": "チャンクが見つかりません"
"noChunksAlt": "チャンクが見つかりません",
"goToDocuments": "ドキュメントへ移動",
"uploadNew": "新規アップロード"
},
"apiKeys": {
"label": "チャットボット",
@@ -101,7 +104,6 @@
},
"messages": "メッセージ",
"tokenUsage": "トークン使用量",
"feedback": "フィードバック",
"filterPlaceholder": "フィルター",
"none": "なし",
"positiveFeedback": "肯定的なフィードバック",
@@ -146,15 +148,20 @@
"queryParameters": "クエリパラメータ",
"body": "ボディ",
"deleteActionWarning": "アクション \"{{name}}\" を削除してもよろしいですか?",
"backToTools": "ツールに戻る",
"backToAllTools": "すべてのツールに戻る",
"save": "保存",
"name": "名",
"type": "タイプ",
"fieldName": "フィールド名",
"fieldType": "フィールドタイプ",
"filledByLLM": "LLMによる入力",
"fieldDescription": "フィールドの説明",
"value": "値",
"addProperty": "プロパティを追加",
"propertyName": "プロパティ",
"noProperties": "プロパティなし"
"propertyName": "新しいプロパティキー",
"add": "追加",
"cancel": "キャンセル",
"addNew": "新規追加",
"name": "名前",
"type": "タイプ"
}
},
"modals": {
@@ -239,6 +246,18 @@
"promptText": "プロンプトテキスト",
"save": "保存",
"nameExists": "名前が既に存在します"
},
"chunk": {
"add": "チャンクを追加",
"edit": "チャンクを編集",
"title": "タイトル",
"enterTitle": "タイトルを入力",
"bodyText": "本文",
"promptText": "プロンプトテキスト",
"update": "更新",
"close": "閉じる",
"delete": "削除",
"deleteConfirmation": "このチャンクを削除してもよろしいですか?"
}
},
"sharedConv": {

View File

@@ -11,6 +11,7 @@
"help": "Помощь",
"emailUs": "Напишите нам",
"documentation": "Документация",
"manageAgents": "Управление агентами",
"demo": [
{
"header": "Узнайте о DocsGPT",
@@ -76,7 +77,9 @@
"backToAll": "Вернуться ко всем документам",
"chunks": "Фрагменты",
"noChunks": "Фрагменты не найдены",
"noChunksAlt": "Фрагменты не найдены"
"noChunksAlt": "Фрагменты не найдены",
"goToDocuments": "Перейти к документам",
"uploadNew": "Загрузить новый"
},
"apiKeys": {
"label": "API ключи",
@@ -137,23 +140,28 @@
"addAction": "Добавить действие",
"noActionsFound": "Действия не найдены",
"url": "URL",
"urlPlaceholder": "Введите url",
"urlPlaceholder": "Введите URL",
"method": "Метод",
"description": "Описание",
"descriptionPlaceholder": "Введите описание",
"headers": "Заголовки",
"queryParameters": "Параметры запроса",
"body": "Тело",
"body": "Тело запроса",
"deleteActionWarning": "Вы уверены, что хотите удалить действие \"{{name}}\"?",
"backToTools": "Вернуться к инструментам",
"backToAllTools": "Вернуться ко всем инструментам",
"save": "Сохранить",
"name": "Имя",
"type": "Тип",
"filledByLLM": "Заполнено LLM",
"fieldName": "Имя поля",
"fieldType": "Тип поля",
"filledByLLM": "Заполняется LLM",
"fieldDescription": "Описание поля",
"value": "Значение",
"addProperty": "Добавить свойство",
"propertyName": "Имя свойства",
"noProperties": "Нет свойств"
"propertyName": "Новый ключ свойства",
"add": "Добавить",
"cancel": "Отмена",
"addNew": "Добавить новое",
"name": "Имя",
"type": "Тип"
}
},
"modals": {
@@ -238,6 +246,18 @@
"promptText": "Текст подсказки",
"save": "Сохранить",
"nameExists": "Название уже существует"
},
"chunk": {
"add": "Добавить фрагмент",
"edit": "Редактировать фрагмент",
"title": "Заголовок",
"enterTitle": "Введите заголовок",
"bodyText": "Текст",
"promptText": "Текст подсказки",
"update": "Обновить",
"close": "Закрыть",
"delete": "Удалить",
"deleteConfirmation": "Вы уверены, что хотите удалить этот фрагмент?"
}
},
"sharedConv": {
@@ -271,7 +291,7 @@
},
"sources": {
"title": "Источники",
"text": "Текст источника",
"text": "Выберите ваши источники",
"link": "Ссылка на источник",
"view_more": "ещё {{count}} источников"
},

View File

@@ -11,6 +11,7 @@
"help": "幫助",
"emailUs": "給我們發電郵",
"documentation": "文件",
"manageAgents": "管理代理",
"demo": [
{
"header": "了解 DocsGPT",
@@ -76,7 +77,9 @@
"backToAll": "返回所有文件",
"chunks": "文本塊",
"noChunks": "未找到文本塊",
"noChunksAlt": "未找到文本塊"
"noChunksAlt": "未找到文本塊",
"goToDocuments": "前往文件",
"uploadNew": "上傳新文件"
},
"apiKeys": {
"label": "聊天機器人",
@@ -145,15 +148,20 @@
"queryParameters": "查詢參數",
"body": "主體",
"deleteActionWarning": "您確定要刪除操作 \"{{name}}\" 嗎?",
"backToTools": "返回工具",
"backToAllTools": "返回所有工具",
"save": "儲存",
"name": "名稱",
"type": "類型",
"filledByLLM": "由LLM填",
"fieldName": "欄位名稱",
"fieldType": "欄位類型",
"filledByLLM": "由LLM填",
"fieldDescription": "欄位描述",
"value": "值",
"addProperty": "新增屬性",
"propertyName": "屬性名稱",
"noProperties": "無屬性"
"propertyName": "屬性",
"add": "新增",
"cancel": "取消",
"addNew": "新增",
"name": "名稱",
"type": "類型"
}
},
"modals": {
@@ -238,6 +246,18 @@
"promptText": "提示文字",
"save": "儲存",
"nameExists": "名稱已存在"
},
"chunk": {
"add": "新增區塊",
"edit": "編輯區塊",
"title": "標題",
"enterTitle": "輸入標題",
"bodyText": "內文",
"promptText": "提示文字",
"update": "更新",
"close": "關閉",
"delete": "刪除",
"deleteConfirmation": "您確定要刪除此區塊嗎?"
}
},
"sharedConv": {

View File

@@ -11,6 +11,7 @@
"help": "帮助",
"emailUs": "给我们发邮件",
"documentation": "文档",
"manageAgents": "管理代理",
"demo": [
{
"header": "了解 DocsGPT",
@@ -72,7 +73,13 @@
},
"actions": "操作",
"view": "查看",
"deleteWarning": "您确定要删除 \"{{name}}\" 吗?"
"deleteWarning": "您确定要删除 \"{{name}}\" 吗?",
"backToAll": "返回所有文档",
"chunks": "文本块",
"noChunks": "未找到文本块",
"noChunksAlt": "未找到文本块",
"goToDocuments": "前往文档",
"uploadNew": "上传新文档"
},
"apiKeys": {
"label": "聊天机器人",
@@ -141,15 +148,20 @@
"queryParameters": "查询参数",
"body": "请求体",
"deleteActionWarning": "您确定要删除操作 \"{{name}}\" 吗?",
"backToTools": "返回工具",
"backToAllTools": "返回所有工具",
"save": "保存",
"name": "名称",
"type": "类型",
"fieldName": "字段名称",
"fieldType": "字段类型",
"filledByLLM": "由LLM填充",
"fieldDescription": "字段描述",
"value": "值",
"addProperty": "添加属性",
"propertyName": "属性名称",
"noProperties": "无属性"
"propertyName": "属性",
"add": "添加",
"cancel": "取消",
"addNew": "添加新的",
"name": "名称",
"type": "类型"
}
},
"modals": {
@@ -234,6 +246,18 @@
"promptText": "提示文本",
"save": "保存",
"nameExists": "名称已存在"
},
"chunk": {
"add": "添加块",
"edit": "编辑块",
"title": "标题",
"enterTitle": "输入标题",
"bodyText": "正文",
"promptText": "提示文本",
"update": "更新",
"close": "关闭",
"delete": "删除",
"deleteConfirmation": "您确定要删除此块吗?"
}
},
"sharedConv": {

View File

@@ -1,9 +1,10 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import Exit from '../assets/exit.svg';
import Input from '../components/Input';
import { ActiveState } from '../models/misc';
import ConfirmationModal from './ConfirmationModal';
import WrapperModal from './WrapperModal';
export default function ChunkModal({
type,
@@ -22,6 +23,7 @@ export default function ChunkModal({
originalText?: string;
handleDelete?: () => void;
}) {
const { t } = useTranslation();
const [title, setTitle] = React.useState('');
const [chunkText, setChunkText] = React.useState('');
const [deleteModal, setDeleteModal] = React.useState<ActiveState>('INACTIVE');
@@ -30,54 +32,40 @@ export default function ChunkModal({
setTitle(originalTitle || '');
setChunkText(originalText || '');
}, [originalTitle, originalText]);
if (type === 'ADD') {
return (
<div
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} fixed left-0 top-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha`}
>
<article className="flex w-11/12 flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E] sm:w-[620px]">
<div className="relative">
<button
className="absolute right-4 top-3 m-2 w-3"
onClick={() => {
setModalState('INACTIVE');
}}
>
<img className="filter dark:invert" src={Exit} />
</button>
<div className="p-6">
if (modalState !== 'ACTIVE') return null;
const content = (
<div>
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
Add Chunk
{t(`modals.chunk.${type === 'ADD' ? 'add' : 'edit'}`)}
</h2>
<div className="relative mt-6 px-3">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Title
</span>
<Input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
borderVariant="thin"
placeholder={'Enter title'}
placeholder={t('modals.chunk.title')}
labelBgClassName="bg-white dark:bg-charleston-green-2"
></Input>
/>
</div>
<div className="relative mt-6 px-3">
<div className="rounded-lg border border-silver pb-1 pt-3 dark:border-silver/40">
<span className="absolute -top-2 left-5 rounded-lg bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Body text
{t('modals.chunk.bodyText')}
</span>
<textarea
id="chunk-body-text"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
className="h-60 max-h-60 w-full resize-none px-3 outline-none dark:bg-transparent dark:text-white"
value={chunkText}
onChange={(e) => setChunkText(e.target.value)}
aria-label="Prompt Text"
aria-label={t('modals.chunk.promptText')}
></textarea>
</div>
</div>
{type === 'ADD' ? (
<div className="mt-8 flex flex-row-reverse gap-1 px-3">
<button
onClick={() => {
@@ -86,7 +74,7 @@ export default function ChunkModal({
}}
className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
>
Add
{t('modals.chunk.add')}
</button>
<button
onClick={() => {
@@ -94,59 +82,10 @@ export default function ChunkModal({
}}
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
>
Close
{t('modals.chunk.close')}
</button>
</div>
</div>
</div>
</article>
</div>
);
} else {
return (
<div
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} fixed left-0 top-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha`}
>
<article className="flex w-11/12 flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E] sm:w-[620px]">
<div className="relative">
<button
className="absolute right-4 top-3 m-2 w-3"
onClick={() => {
setModalState('INACTIVE');
}}
>
<img className="filter dark:invert" src={Exit} />
</button>
<div className="p-6">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
Edit Chunk
</h2>
<div className="relative mt-6 px-3">
<Input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
borderVariant="thin"
placeholder={'Enter title'}
labelBgClassName="bg-white dark:bg-charleston-green-2"
></Input>
</div>
<div className="relative mt-6 px-3">
<div className="rounded-lg border border-silver pb-1 pt-3 dark:border-silver/40">
<span className="absolute -top-2 left-5 rounded-lg bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Body text
</span>
<textarea
id="chunk-body-text"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
value={chunkText}
onChange={(e) => setChunkText(e.target.value)}
aria-label="Prompt Text"
></textarea>
</div>
</div>
) : (
<div className="mt-8 flex w-full items-center justify-between px-3">
<button
className="text-nowrap rounded-full border border-solid border-red-500 px-5 py-2 text-sm text-red-500 hover:bg-red-500 hover:text-white"
@@ -154,7 +93,7 @@ export default function ChunkModal({
setDeleteModal('ACTIVE');
}}
>
Delete
{t('modals.chunk.delete')}
</button>
<div className="flex flex-row-reverse gap-1">
<button
@@ -164,7 +103,7 @@ export default function ChunkModal({
}}
className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
>
Update
{t('modals.chunk.update')}
</button>
<button
onClick={() => {
@@ -172,15 +111,26 @@ export default function ChunkModal({
}}
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
>
Close
{t('modals.chunk.close')}
</button>
</div>
</div>
)}
</div>
</div>
</article>
);
return (
<>
<WrapperModal
close={() => setModalState('INACTIVE')}
className="sm:w-[620px]"
>
{content}
</WrapperModal>
{type === 'EDIT' && (
<ConfirmationModal
message="Are you sure you want to delete this chunk?"
message={t('modals.chunk.deleteConfirmation')}
modalState={deleteModal}
setModalState={setDeleteModal}
handleSubmit={
@@ -190,9 +140,9 @@ export default function ChunkModal({
/* no-op */
}
}
submitLabel="Delete"
submitLabel={t('modals.chunk.delete')}
/>
</div>
)}
</>
);
}
}

View File

@@ -51,7 +51,7 @@ function AddPrompt({
</label>
<textarea
id="new-prompt-content"
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
className="h-56 w-full resize-none rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
value={newPromptContent}
onChange={(e) => setNewPromptContent(e.target.value)}
aria-label="Prompt Text"
@@ -123,7 +123,7 @@ function EditPrompt({
</label>
<textarea
id="edit-prompt-content"
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
className="h-56 w-full resize-none rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
value={editPromptContent}
onChange={(e) => setEditPromptContent(e.target.value)}
aria-label="Prompt Text"

View File

@@ -450,7 +450,7 @@ export default function Documents({
options={getActionOptions(index, document)}
anchorRef={getMenuRef(docId)}
position="bottom-left"
offset={{ x: 48, y: -24 }}
offset={{ x: 48, y: 0 }}
className="z-50"
/>
</div>

View File

@@ -169,13 +169,13 @@ export default function ToolConfig({
>
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
</button>
<p className="mt-px">Back to all tools</p>
<p className="mt-px">{t('settings.tools.backToAllTools')}</p>
</div>
<button
className="text-nowrap rounded-full bg-purple-30 px-3 py-2 text-xs text-white hover:bg-violets-are-blue sm:px-4 sm:py-2"
onClick={handleSaveChanges}
>
Save
{t('settings.tools.save')}
</button>
</div>
{/* Custom name section */}
@@ -282,7 +282,7 @@ export default function ToolConfig({
<Input
type="text"
className="w-full"
placeholder="Enter description"
placeholder={t('settings.tools.descriptionPlaceholder')}
value={action.description}
onChange={(e) => {
setTool({
@@ -305,11 +305,11 @@ export default function ToolConfig({
<table className="table-default">
<thead>
<tr>
<th>Field Name</th>
<th>Field Type</th>
<th>Filled by LLM</th>
<th>FIeld description</th>
<th>Value</th>
<th>{t('settings.tools.fieldName')}</th>
<th>{t('settings.tools.fieldType')}</th>
<th>{t('settings.tools.filledByLLM')}</th>
<th>{t('settings.tools.fieldDescription')}</th>
<th>{t('settings.tools.value')}</th>
</tr>
</thead>
<tbody>
@@ -619,7 +619,7 @@ function APIToolConfig({
<div className="mt-4 px-5 py-2">
<div className="relative w-full">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
Method
{t('settings.tools.method')}
</span>
<Dropdown
options={['GET', 'POST', 'PUT', 'DELETE']}
@@ -984,7 +984,7 @@ function APIActionTable({
handleAddProperty();
}
}}
placeholder="New property key"
placeholder={t('settings.tools.propertyName')}
className="flex w-full min-w-[130.5px] items-start rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
/>
</td>
@@ -993,15 +993,13 @@ function APIActionTable({
onClick={handleAddProperty}
className="mr-1 rounded-full bg-purple-30 px-5 py-[4px] text-sm text-white hover:bg-violets-are-blue"
>
{' '}
Add{' '}
{t('settings.tools.add')}
</button>
<button
onClick={handleAddPropertyCancel}
className="rounded-full border border-solid border-red-500 px-5 py-[4px] text-sm text-red-500 hover:bg-red-500 hover:text-white"
>
{' '}
Cancel{' '}
{t('settings.tools.cancel')}
</button>
</td>
<td
@@ -1020,7 +1018,7 @@ function APIActionTable({
onClick={() => handleAddPropertyStart(section)}
className="flex items-start text-nowrap rounded-full border border-solid border-violets-are-blue px-5 py-[4px] text-sm text-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white"
>
Add New Field
{t('settings.tools.addNew')}
</button>
</td>
<td