diff --git a/application/api/user/routes.py b/application/api/user/routes.py
index ae653c39..5c9975fb 100644
--- a/application/api/user/routes.py
+++ b/application/api/user/routes.py
@@ -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,32 +87,46 @@ def generate_date_range(start_date, end_date):
def ensure_user_doc(user_id):
- user_doc = users_collection.find_one({"user_id": user_id})
+ default_prefs = {
+ "pinned": [],
+ "shared_with_me": [],
+ }
- 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", {})
+ 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 "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}}
- )
- user_doc["agent_preferences"] = preferences
return user_doc
+def resolve_tool_details(tool_ids):
+ tools = user_tools_collection.find(
+ {"_id": {"$in": [ObjectId(tid) for tid in tool_ids]}}
+ )
+ 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):
"""
Get the Vector Store
@@ -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:
- users_collection.update_one(
- {"user_id": user_id},
- {"$pull": {"agent_preferences.hidden_shared": agent_id}},
- )
- action = "unhidden"
- else:
- users_collection.update_one(
- {"user_id": user_id},
- {"$addToSet": {"agent_preferences.hidden_shared": agent_id}},
- )
- action = "hidden"
+ ensure_user_doc(user_id)
+ users_collection.update_one(
+ {"user_id": user_id},
+ {
+ "$pull": {
+ "agent_preferences.shared_with_me": agent_id,
+ "agent_preferences.pinned": agent_id,
+ }
+ },
+ )
+
+ 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)
diff --git a/application/requirements.txt b/application/requirements.txt
index 3891c852..3778d941 100644
--- a/application/requirements.txt
+++ b/application/requirements.txt
@@ -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
diff --git a/application/retriever/classic_rag.py b/application/retriever/classic_rag.py
index 2b4e6df7..9416b4f7 100644
--- a/application/retriever/classic_rag.py
+++ b/application/retriever/classic_rag.py
@@ -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(
diff --git a/application/retriever/retriever_creator.py b/application/retriever/retriever_creator.py
index 07be373d..26cb41ca 100644
--- a/application/retriever/retriever_creator.py
+++ b/application/retriever/retriever_creator.py
@@ -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)
\ No newline at end of file
+ return retiever_class(*args, **kwargs)
diff --git a/application/vectorstore/faiss.py b/application/vectorstore/faiss.py
index ce455bd8..2c1fcb93 100644
--- a/application/vectorstore/faiss.py
+++ b/application/vectorstore/faiss.py
@@ -32,22 +32,26 @@ class FaissStore(BaseVectorStore):
with tempfile.TemporaryDirectory() as temp_dir:
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)
-
+
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(
temp_dir, self.embeddings, allow_dangerous_deserialization=True
)
diff --git a/frontend/src/About.tsx b/frontend/src/About.tsx
deleted file mode 100644
index d2bbaac4..00000000
--- a/frontend/src/About.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-//TODO - Add hyperlinks to text
-//TODO - Styling
-import DocsGPT3 from './assets/cute_docsgpt3.svg';
-
-export default function About() {
- return (
-
-
-
-
About DocsGPT
-
-
-
- Find the information in your documentation through AI-powered
-
- {' '}
- open-source{' '}
-
- chatbot. Powered by GPT-3, Faiss and LangChain.
-
-
-
-
- If you want to add your own documentation, please follow the
- instruction below:
-
-
- 1. Navigate to{' '}
-
- {' '}
- /application
- {' '}
- folder
-
-
- 2. Install dependencies from{' '}
-
- pip install -r requirements.txt
-
-
-
- 3. Prepare a{' '}
- .env {' '}
- file. Copy{' '}
-
- .env_sample
- {' '}
- and create{' '}
- .env {' '}
- with your OpenAI API token
-
-
- 4. Run the app with{' '}
-
- python app.py
-
-
-
-
-
- Currently It uses{' '}
- DocsGPT {' '}
- documentation, so it will respond to information relevant to{' '}
- DocsGPT . If you
- want to train it on different documentation - please follow
-
- {' '}
- this guide
-
- .
-
-
-
- If you want to launch it on your own server - follow
-
- {' '}
- this guide
-
- .
-
-
-
- );
-}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 1bba5f44..9c5de77f 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -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 (
@@ -38,7 +38,7 @@ function MainLayout() {
} />
- } />
} />
} />
} />
+ } />
} />
diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx
index 13aed4f2..300253b8 100644
--- a/frontend/src/Navigation.tsx
+++ b/frontend/src/Navigation.tsx
@@ -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) {
{
- if (isMobile) {
+ if (isMobile || isTablet) {
setNavOpen(!navOpen);
}
resetConversation();
@@ -411,7 +411,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
{
dispatch(setSelectedAgent(null));
+ if (isMobile || isTablet) {
+ setNavOpen(false);
+ }
navigate('/agents');
}}
>
@@ -444,7 +447,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
/>
- Manage Agents
+ {t('manageAgents')}
@@ -452,7 +455,13 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
) : (
navigate('/agents')}
+ onClick={() => {
+ if (isMobile || isTablet) {
+ setNavOpen(false);
+ }
+ dispatch(setSelectedAgent(null));
+ navigate('/agents');
+ }}
>
- Manage Agents
+ {t('manageAgents')}
)}
@@ -498,8 +507,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
{
- if (isMobile) {
- setNavOpen(!navOpen);
+ if (isMobile || isTablet) {
+ setNavOpen(false);
}
resetConversation();
}}
diff --git a/frontend/src/agents/SharedAgent.tsx b/frontend/src/agents/SharedAgent.tsx
index 8c7e76c9..d1d2d370 100644
--- a/frontend/src/agents/SharedAgent.tsx
+++ b/frontend/src/agents/SharedAgent.tsx
@@ -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() {
);
}
-
-function SharedAgentCard({ agent }: { agent: Agent }) {
- return (
-
-
-
-
-
-
-
- {agent.name}
-
-
- {agent.description}
-
-
-
-
- {agent.shared_metadata?.shared_by && (
-
- by {agent.shared_metadata.shared_by}
-
- )}
- {agent.shared_metadata?.shared_at && (
-
- 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,
- })}
-
- )}
-
- {agent.tools.length > 0 && (
-
-
- Connected Tools
-
-
- {agent.tools.map((tool, index) => (
-
- {' '}
- {tool}
-
- ))}
-
-
- )}
-
- );
-}
diff --git a/frontend/src/agents/SharedAgentCard.tsx b/frontend/src/agents/SharedAgentCard.tsx
new file mode 100644
index 00000000..bf542ca9
--- /dev/null
+++ b/frontend/src/agents/SharedAgentCard.tsx
@@ -0,0 +1,69 @@
+import Robot from '../assets/robot.svg';
+import { Agent } from './types';
+
+export default function SharedAgentCard({ agent }: { agent: Agent }) {
+ return (
+
+
+
+
+
+
+
+ {agent.name}
+
+
+ {agent.description}
+
+
+
+ {agent.shared_metadata && (
+
+ {agent.shared_metadata?.shared_by && (
+
+ by {agent.shared_metadata.shared_by}
+
+ )}
+ {agent.shared_metadata?.shared_at && (
+
+ 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,
+ },
+ )}
+
+ )}
+
+ )}
+ {agent.tool_details && agent.tool_details.length > 0 && (
+
+
+ Connected Tools
+
+
+ {agent.tool_details.map((tool, index) => (
+
+ {' '}
+ {tool.name}
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/frontend/src/agents/SharedAgentGate.tsx b/frontend/src/agents/SharedAgentGate.tsx
new file mode 100644
index 00000000..877b6b1f
--- /dev/null
+++ b/frontend/src/agents/SharedAgentGate.tsx
@@ -0,0 +1,7 @@
+import { Navigate, useParams } from 'react-router-dom';
+
+export default function SharedAgentGate() {
+ const { agentId } = useParams();
+
+ return ;
+}
diff --git a/frontend/src/agents/index.tsx b/frontend/src/agents/index.tsx
index 2f8bf484..8fe1afc9 100644
--- a/frontend/src/agents/index.tsx
+++ b/frontend/src/agents/index.tsx
@@ -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,
diff --git a/frontend/src/agents/types/index.ts b/frontend/src/agents/types/index.ts
index fe3cb418..4a1af145 100644
--- a/frontend/src/agents/types/index.ts
+++ b/frontend/src/agents/types/index.ts
@@ -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;
diff --git a/frontend/src/api/endpoints.ts b/frontend/src/api/endpoints.ts
index 72db0595..bb98f02a 100644
--- a/frontend/src/api/endpoints.ts
+++ b/frontend/src/api/endpoints.ts
@@ -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',
diff --git a/frontend/src/api/services/userService.ts b/frontend/src/api/services/userService.ts
index 9dd679a5..564c3b97 100644
--- a/frontend/src/api/services/userService.ts
+++ b/frontend/src/api/services/userService.ts
@@ -41,8 +41,8 @@ const userService = {
apiClient.get(endpoints.USER.SHARED_AGENTS, token),
shareAgent: (data: any, token: string | null): Promise =>
apiClient.put(endpoints.USER.SHARE_AGENT, data, token),
- hideSharedAgent: (id: string, token: string | null): Promise =>
- apiClient.delete(endpoints.USER.HIDE_SHARED_AGENT(id), token),
+ removeSharedAgent: (id: string, token: string | null): Promise =>
+ apiClient.delete(endpoints.USER.REMOVE_SHARED_AGENT(id), token),
getAgentWebhook: (id: string, token: string | null): Promise =>
apiClient.get(endpoints.USER.AGENT_WEBHOOK(id), token),
getPrompts: (token: string | null): Promise =>
diff --git a/frontend/src/components/SourcesPopup.tsx b/frontend/src/components/SourcesPopup.tsx
index 83966730..bdfb7538 100644
--- a/frontend/src/components/SourcesPopup.tsx
+++ b/frontend/src/components/SourcesPopup.tsx
@@ -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')}
@@ -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')}
diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx
index 753a3725..4f34c26c 100644
--- a/frontend/src/conversation/Conversation.tsx
+++ b/frontend/src/conversation/Conversation.tsx
@@ -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 ? (
+
+
+
+ ) : undefined
+ }
/>
diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts
index bffcfb5c..1b9f9b87 100644
--- a/frontend/src/hooks/index.ts
+++ b/frontend/src/hooks/index.ts
@@ -35,8 +35,8 @@ export function useOutsideAlerter
(
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);
diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json
index d5aa7d85..7fda12c8 100644
--- a/frontend/src/locale/en.json
+++ b/frontend/src/locale/en.json
@@ -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": {
diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json
index 90ce0844..2f0260b0 100644
--- a/frontend/src/locale/es.json
+++ b/frontend/src/locale/es.json
@@ -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",
diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json
index 18e3bf4f..3c1cb64b 100644
--- a/frontend/src/locale/jp.json
+++ b/frontend/src/locale/jp.json
@@ -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": {
diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json
index 1a3b8848..8d62a577 100644
--- a/frontend/src/locale/ru.json
+++ b/frontend/src/locale/ru.json
@@ -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}} источников"
},
diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json
index f20ade6b..366d0f56 100644
--- a/frontend/src/locale/zh-TW.json
+++ b/frontend/src/locale/zh-TW.json
@@ -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": {
diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json
index 22f144dd..6d4e590c 100644
--- a/frontend/src/locale/zh.json
+++ b/frontend/src/locale/zh.json
@@ -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": {
diff --git a/frontend/src/modals/ChunkModal.tsx b/frontend/src/modals/ChunkModal.tsx
index ab51fdee..4e229980 100644
--- a/frontend/src/modals/ChunkModal.tsx
+++ b/frontend/src/modals/ChunkModal.tsx
@@ -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('INACTIVE');
@@ -30,157 +32,105 @@ export default function ChunkModal({
setTitle(originalTitle || '');
setChunkText(originalText || '');
}, [originalTitle, originalText]);
- if (type === 'ADD') {
- return (
-
-
-
-
{
- setModalState('INACTIVE');
- }}
- >
-
-
-
-
- Add Chunk
-
-
-
- Title
-
- setTitle(e.target.value)}
- borderVariant="thin"
- placeholder={'Enter title'}
- labelBgClassName="bg-white dark:bg-charleston-green-2"
- >
-
-
-
- {
- handleSubmit(title, chunkText);
- setModalState('INACTIVE');
- }}
- className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
- >
- Add
-
- {
- setModalState('INACTIVE');
- }}
- 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
-
-
-
-
-
+
+ if (modalState !== 'ACTIVE') return null;
+
+ const content = (
+
+
+ {t(`modals.chunk.${type === 'ADD' ? 'add' : 'edit'}`)}
+
+
+ setTitle(e.target.value)}
+ borderVariant="thin"
+ placeholder={t('modals.chunk.title')}
+ labelBgClassName="bg-white dark:bg-charleston-green-2"
+ />
- );
- } else {
- return (
-
-
-
+
+
+
+ {t('modals.chunk.bodyText')}
+
+
+
+
+
+ {type === 'ADD' ? (
+
+ {
+ handleSubmit(title, chunkText);
+ setModalState('INACTIVE');
+ }}
+ className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
+ >
+ {t('modals.chunk.add')}
+
+ {
+ setModalState('INACTIVE');
+ }}
+ 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"
+ >
+ {t('modals.chunk.close')}
+
+
+ ) : (
+
+
{
+ setDeleteModal('ACTIVE');
+ }}
+ >
+ {t('modals.chunk.delete')}
+
+
+
{
+ handleSubmit(title, chunkText);
+ setModalState('INACTIVE');
+ }}
+ className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
+ >
+ {t('modals.chunk.update')}
+
{
setModalState('INACTIVE');
}}
+ 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"
>
-
+ {t('modals.chunk.close')}
-
-
- Edit Chunk
-
-
- setTitle(e.target.value)}
- borderVariant="thin"
- placeholder={'Enter title'}
- labelBgClassName="bg-white dark:bg-charleston-green-2"
- >
-
-
-
-
{
- setDeleteModal('ACTIVE');
- }}
- >
- Delete
-
-
- {
- handleSubmit(title, chunkText);
- setModalState('INACTIVE');
- }}
- className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue"
- >
- Update
-
- {
- setModalState('INACTIVE');
- }}
- 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
-
-
-
-
-
+
+ )}
+
+ );
+
+ return (
+ <>
+ setModalState('INACTIVE')}
+ className="sm:w-[620px]"
+ >
+ {content}
+
+
+ {type === 'EDIT' && (
-
- );
- }
+ )}
+ >
+ );
}
diff --git a/frontend/src/preferences/PromptsModal.tsx b/frontend/src/preferences/PromptsModal.tsx
index b5e028bd..f61ba4ee 100644
--- a/frontend/src/preferences/PromptsModal.tsx
+++ b/frontend/src/preferences/PromptsModal.tsx
@@ -51,7 +51,7 @@ function AddPrompt({
diff --git a/frontend/src/settings/ToolConfig.tsx b/frontend/src/settings/ToolConfig.tsx
index 2194f93f..7d738842 100644
--- a/frontend/src/settings/ToolConfig.tsx
+++ b/frontend/src/settings/ToolConfig.tsx
@@ -169,13 +169,13 @@ export default function ToolConfig({
>
-
Back to all tools
+
{t('settings.tools.backToAllTools')}
- Save
+ {t('settings.tools.save')}
{/* Custom name section */}
@@ -282,7 +282,7 @@ export default function ToolConfig({
{
setTool({
@@ -305,11 +305,11 @@ export default function ToolConfig({
- Field Name
- Field Type
- Filled by LLM
- FIeld description
- Value
+ {t('settings.tools.fieldName')}
+ {t('settings.tools.fieldType')}
+ {t('settings.tools.filledByLLM')}
+ {t('settings.tools.fieldDescription')}
+ {t('settings.tools.value')}
@@ -619,7 +619,7 @@ function APIToolConfig({
- Method
+ {t('settings.tools.method')}
@@ -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')}
- {' '}
- Cancel{' '}
+ {t('settings.tools.cancel')}
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')}