mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge branch 'arc53:main' into main
This commit is contained in:
@@ -42,6 +42,7 @@ from application.utils import (
|
||||
generate_image_url,
|
||||
safe_filename,
|
||||
validate_function_name,
|
||||
validate_required_fields,
|
||||
)
|
||||
from application.vectorstore.vector_creator import VectorCreator
|
||||
|
||||
@@ -1232,7 +1233,6 @@ class CreateAgent(Resource):
|
||||
if request.content_type == "application/json":
|
||||
data = request.get_json()
|
||||
else:
|
||||
print(request.form)
|
||||
data = request.form.to_dict()
|
||||
if "tools" in data:
|
||||
try:
|
||||
@@ -1240,11 +1240,18 @@ class CreateAgent(Resource):
|
||||
except json.JSONDecodeError:
|
||||
data["tools"] = []
|
||||
print(f"Received data: {data}")
|
||||
|
||||
if data.get("status") not in ["draft", "published"]:
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "Invalid status"}), 400
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": "Status must be either 'draft' or 'published'",
|
||||
}
|
||||
),
|
||||
400,
|
||||
)
|
||||
required_fields = []
|
||||
|
||||
if data.get("status") == "published":
|
||||
required_fields = [
|
||||
"name",
|
||||
@@ -1255,11 +1262,16 @@ class CreateAgent(Resource):
|
||||
"prompt_id",
|
||||
"agent_type",
|
||||
]
|
||||
validate_fields = ["name", "description", "prompt_id", "agent_type"]
|
||||
else:
|
||||
required_fields = ["name"]
|
||||
validate_fields = []
|
||||
missing_fields = check_required_fields(data, required_fields)
|
||||
invalid_fields = validate_required_fields(data, validate_fields)
|
||||
if missing_fields:
|
||||
return missing_fields
|
||||
if invalid_fields:
|
||||
return invalid_fields
|
||||
|
||||
image_url, error = handle_image_upload(request, "", user, storage)
|
||||
if error:
|
||||
@@ -1268,7 +1280,7 @@ class CreateAgent(Resource):
|
||||
)
|
||||
|
||||
try:
|
||||
key = str(uuid.uuid4())
|
||||
key = str(uuid.uuid4()) if data.get("status") == "published" else ""
|
||||
new_agent = {
|
||||
"user": user,
|
||||
"name": data.get("name"),
|
||||
@@ -1453,6 +1465,7 @@ class UpdateAgent(Resource):
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "No update data provided"}), 400
|
||||
)
|
||||
newly_generated_key = None
|
||||
final_status = update_fields.get("status", existing_agent.get("status"))
|
||||
if final_status == "published":
|
||||
required_published_fields = [
|
||||
@@ -1482,6 +1495,10 @@ class UpdateAgent(Resource):
|
||||
),
|
||||
400,
|
||||
)
|
||||
|
||||
if not existing_agent.get("key"):
|
||||
newly_generated_key = str(uuid.uuid4())
|
||||
update_fields["key"] = newly_generated_key
|
||||
update_fields["updatedAt"] = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
try:
|
||||
@@ -1504,7 +1521,7 @@ class UpdateAgent(Resource):
|
||||
jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"message": "Agent found, but no changes were applied.",
|
||||
"message": "Agent found, but no changes were applied",
|
||||
}
|
||||
),
|
||||
304,
|
||||
@@ -1517,14 +1534,15 @@ class UpdateAgent(Resource):
|
||||
jsonify({"success": False, "message": "Database error during update"}),
|
||||
500,
|
||||
)
|
||||
response_data = {
|
||||
"success": True,
|
||||
"id": agent_id,
|
||||
"message": "Agent updated successfully",
|
||||
}
|
||||
if newly_generated_key:
|
||||
response_data["key"] = newly_generated_key
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"id": agent_id,
|
||||
"message": "Agent updated successfully",
|
||||
}
|
||||
),
|
||||
jsonify(response_data),
|
||||
200,
|
||||
)
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ def check_required_fields(data, required_fields):
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Missing fields: {', '.join(missing_fields)}",
|
||||
"message": f"Missing required fields: {', '.join(missing_fields)}",
|
||||
}
|
||||
),
|
||||
400,
|
||||
@@ -88,6 +88,29 @@ def check_required_fields(data, required_fields):
|
||||
return None
|
||||
|
||||
|
||||
def validate_required_fields(data, required_fields):
|
||||
missing_fields = []
|
||||
empty_fields = []
|
||||
|
||||
for field in required_fields:
|
||||
if field not in data:
|
||||
missing_fields.append(field)
|
||||
elif not data[field]:
|
||||
empty_fields.append(field)
|
||||
|
||||
errors = []
|
||||
if missing_fields:
|
||||
errors.append(f"Missing required fields: {', '.join(missing_fields)}")
|
||||
if empty_fields:
|
||||
errors.append(f"Empty values in required fields: {', '.join(empty_fields)}")
|
||||
|
||||
if errors:
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": " | ".join(errors)}), 400
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_hash(data):
|
||||
return hashlib.md5(data.encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
|
||||
15
frontend/package-lock.json
generated
15
frontend/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"i18next": "^24.2.0",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mermaid": "^11.6.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^19.1.0",
|
||||
@@ -32,6 +33,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/mermaid": "^9.1.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
@@ -2302,6 +2304,13 @@
|
||||
"integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||
@@ -7226,6 +7235,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"i18next": "^24.2.0",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mermaid": "^11.6.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^19.1.0",
|
||||
@@ -43,6 +44,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/mermaid": "^9.1.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
|
||||
@@ -110,7 +110,7 @@ export default function AgentPreview() {
|
||||
}, [queries]);
|
||||
return (
|
||||
<div>
|
||||
<div className="flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden dark:bg-raisin-black">
|
||||
<div className="dark:bg-raisin-black flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden">
|
||||
<div className="h-[512px] w-full overflow-y-auto">
|
||||
<ConversationMessages
|
||||
handleQuestion={handleQuestion}
|
||||
@@ -128,7 +128,7 @@ export default function AgentPreview() {
|
||||
showToolButton={selectedAgent ? false : true}
|
||||
autoFocus={false}
|
||||
/>
|
||||
<p className="w-full self-center bg-transparent pt-2 text-center text-xs text-gray-4000 dark:text-sonic-silver md:inline">
|
||||
<p className="text-gray-4000 dark:text-sonic-silver w-full self-center bg-transparent pt-2 text-center text-xs md:inline">
|
||||
This is a preview of the agent. You can publish it to start using it
|
||||
in conversations.
|
||||
</p>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
@@ -8,6 +9,7 @@ import SourceIcon from '../assets/source.svg';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
import { FileUpload } from '../components/FileUpload';
|
||||
import MultiSelectPopup, { OptionType } from '../components/MultiSelectPopup';
|
||||
import Spinner from '../components/Spinner';
|
||||
import AgentDetailsModal from '../modals/AgentDetailsModal';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import { ActiveState, Doc, Prompt } from '../models/misc';
|
||||
@@ -66,34 +68,41 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
useState<ActiveState>('INACTIVE');
|
||||
const [agentDetails, setAgentDetails] = useState<ActiveState>('INACTIVE');
|
||||
const [addPromptModal, setAddPromptModal] = useState<ActiveState>('INACTIVE');
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [draftLoading, setDraftLoading] = useState(false);
|
||||
const [publishLoading, setPublishLoading] = useState(false);
|
||||
|
||||
const initialAgentRef = useRef<Agent | null>(null);
|
||||
const sourceAnchorButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const toolAnchorButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const modeConfig = {
|
||||
new: {
|
||||
heading: 'New Agent',
|
||||
buttonText: 'Create Agent',
|
||||
buttonText: 'Publish',
|
||||
showDelete: false,
|
||||
showSaveDraft: true,
|
||||
showLogs: false,
|
||||
showAccessDetails: false,
|
||||
trackChanges: false,
|
||||
},
|
||||
edit: {
|
||||
heading: 'Edit Agent',
|
||||
buttonText: 'Save Changes',
|
||||
buttonText: 'Save',
|
||||
showDelete: true,
|
||||
showSaveDraft: false,
|
||||
showLogs: true,
|
||||
showAccessDetails: true,
|
||||
trackChanges: true,
|
||||
},
|
||||
draft: {
|
||||
heading: 'New Agent (Draft)',
|
||||
buttonText: 'Publish Draft',
|
||||
buttonText: 'Publish',
|
||||
showDelete: true,
|
||||
showSaveDraft: true,
|
||||
showLogs: false,
|
||||
showAccessDetails: false,
|
||||
trackChanges: false,
|
||||
},
|
||||
};
|
||||
const chunks = ['0', '2', '4', '6', '8', '10'];
|
||||
@@ -144,23 +153,27 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
else formData.append('tools', '[]');
|
||||
|
||||
try {
|
||||
setDraftLoading(true);
|
||||
const response =
|
||||
effectiveMode === 'new'
|
||||
? await userService.createAgent(formData, token)
|
||||
: await userService.updateAgent(agent.id || '', formData, token);
|
||||
if (!response.ok) throw new Error('Failed to create agent draft');
|
||||
const data = await response.json();
|
||||
if (effectiveMode === 'new') {
|
||||
setEffectiveMode('draft');
|
||||
setAgent((prev) => ({
|
||||
...prev,
|
||||
id: data.id,
|
||||
image: data.image || prev.image,
|
||||
}));
|
||||
}
|
||||
|
||||
const updatedAgent = {
|
||||
...agent,
|
||||
id: data.id || agent.id,
|
||||
image: data.image || agent.image,
|
||||
};
|
||||
setAgent(updatedAgent);
|
||||
|
||||
if (effectiveMode === 'new') setEffectiveMode('draft');
|
||||
} catch (error) {
|
||||
console.error('Error saving draft:', error);
|
||||
throw new Error('Failed to save draft');
|
||||
} finally {
|
||||
setDraftLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,26 +194,34 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
else formData.append('tools', '[]');
|
||||
|
||||
try {
|
||||
setPublishLoading(true);
|
||||
const response =
|
||||
effectiveMode === 'new'
|
||||
? await userService.createAgent(formData, token)
|
||||
: await userService.updateAgent(agent.id || '', formData, token);
|
||||
if (!response.ok) throw new Error('Failed to publish agent');
|
||||
const data = await response.json();
|
||||
if (data.id) setAgent((prev) => ({ ...prev, id: data.id }));
|
||||
if (data.key) setAgent((prev) => ({ ...prev, key: data.key }));
|
||||
|
||||
const updatedAgent = {
|
||||
...agent,
|
||||
id: data.id || agent.id,
|
||||
key: data.key || agent.key,
|
||||
status: 'published',
|
||||
image: data.image || agent.image,
|
||||
};
|
||||
setAgent(updatedAgent);
|
||||
initialAgentRef.current = updatedAgent;
|
||||
|
||||
if (effectiveMode === 'new' || effectiveMode === 'draft') {
|
||||
setEffectiveMode('edit');
|
||||
setAgent((prev) => ({
|
||||
...prev,
|
||||
status: 'published',
|
||||
image: data.image || prev.image,
|
||||
}));
|
||||
setAgentDetails('ACTIVE');
|
||||
}
|
||||
setImageFile(null);
|
||||
} catch (error) {
|
||||
console.error('Error publishing agent:', error);
|
||||
throw new Error('Failed to publish agent');
|
||||
} finally {
|
||||
setPublishLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -243,6 +264,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
if (data.tools) setSelectedToolIds(new Set(data.tools));
|
||||
if (data.status === 'draft') setEffectiveMode('draft');
|
||||
setAgent(data);
|
||||
initialAgentRef.current = data;
|
||||
};
|
||||
getAgent();
|
||||
}
|
||||
@@ -285,7 +307,19 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (isPublishable()) dispatch(setSelectedAgent(agent));
|
||||
}, [agent, dispatch]);
|
||||
|
||||
if (!modeConfig[effectiveMode].trackChanges) {
|
||||
setHasChanges(true);
|
||||
return;
|
||||
}
|
||||
if (!initialAgentRef.current) {
|
||||
setHasChanges(false);
|
||||
return;
|
||||
}
|
||||
const isChanged =
|
||||
!isEqual(agent, initialAgentRef.current) || imageFile !== null;
|
||||
setHasChanges(isChanged);
|
||||
}, [agent, dispatch, effectiveMode, imageFile]);
|
||||
return (
|
||||
<div className="p-4 md:p-12">
|
||||
<div className="flex items-center gap-3 px-4">
|
||||
@@ -321,10 +355,16 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
{modeConfig[effectiveMode].showSaveDraft && (
|
||||
<button
|
||||
className="hover:bg-vi</button>olets-are-blue border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="hover:bg-vi</button>olets-are-blue border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue w-28 rounded-3xl border border-solid py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={handleSaveDraft}
|
||||
>
|
||||
Save Draft
|
||||
<span className="flex items-center justify-center transition-all duration-200">
|
||||
{draftLoading ? (
|
||||
<Spinner size="small" color="#976af3" />
|
||||
) : (
|
||||
'Save Draft'
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{modeConfig[effectiveMode].showAccessDetails && (
|
||||
@@ -345,11 +385,17 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
disabled={!isPublishable()}
|
||||
className={`${!isPublishable() && 'cursor-not-allowed opacity-30'} bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm font-medium text-white`}
|
||||
disabled={!isPublishable() || !hasChanges}
|
||||
className={`${!isPublishable() || !hasChanges ? 'cursor-not-allowed opacity-30' : ''} bg-purple-30 hover:bg-violets-are-blue flex w-28 items-center justify-center rounded-3xl py-2 text-sm font-medium text-white`}
|
||||
onClick={handlePublish}
|
||||
>
|
||||
Publish
|
||||
<span className="flex items-center justify-center transition-all duration-200">
|
||||
{publishLoading ? (
|
||||
<Spinner size="small" color="white" />
|
||||
) : (
|
||||
modeConfig[effectiveMode].buttonText
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -654,7 +700,7 @@ function AddPromptModal({
|
||||
setNewPromptContent('');
|
||||
onSelect?.(newPromptName, newPrompt.id, newPromptContent);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error('Error adding prompt:', error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user