diff --git a/python/helpers/settings.py b/python/helpers/settings.py index 045575573..06d9b312e 100644 --- a/python/helpers/settings.py +++ b/python/helpers/settings.py @@ -78,7 +78,7 @@ def convert_out(settings: Settings) -> dict[str, Any]: } ) - chat_model_seciton = { + chat_model_section = { "title": "Chat Model", "description": "Selection and settings for main chat model used by Agent Zero", "fields": chat_model_fields, @@ -129,13 +129,51 @@ def convert_out(settings: Settings) -> dict[str, Any]: } ) - util_model_seciton = { + util_model_section = { "title": "Utility model", "description": "Smaller, cheaper, faster model for handling utility tasks like organizing memory, preparing prompts, summarizing.", "fields": util_model_fields, } - result = {"sections": [chat_model_seciton, util_model_seciton]} + # embedding model section + embed_model_fields = [] + embed_model_fields.append( + { + "id": "embed_model_provider", + "title": "Embedding model provider", + "description": "Select provider for embedding model used by the framework", + "type": "select", + "value": settings["embed_model_provider"], + "options": [{"value": p.name, "label": p.value} for p in ModelProvider], + } + ) + embed_model_fields.append( + { + "id": "embed_model_name", + "title": "Embedding model name", + "description": "Exact name of model from selected provider", + "type": "input", + "value": settings["embed_model_name"], + } + ) + + embed_model_fields.append( + { + "id": "embed_model_kwargs", + "title": "Embedding model additional parameters", + "description": "Any other parameters supported by the model. Format is KEY=VALUE on individual lines, just like .env file.", + "type": "textarea", + "value": _dict_to_env(settings["embed_model_kwargs"]), + } + ) + + embed_model_section = { + "title": "Embedding Model", + "description": "Settings for the embedding model used by Agent Zero.", + "fields": embed_model_fields, + } + + result = {"sections": [chat_model_section, util_model_section, embed_model_section]} return result def convert_in(settings: dict[str, Any]) -> Settings: diff --git a/run_ui.py b/run_ui.py index 713084068..3b152092e 100644 --- a/run_ui.py +++ b/run_ui.py @@ -13,6 +13,7 @@ from python.helpers.files import get_abs_path from python.helpers.print_style import PrintStyle from python.helpers.dotenv import load_dotenv from python.helpers import persist_chat, settings +from werkzeug.utils import secure_filename # initialize the internal Flask server @@ -65,6 +66,61 @@ def requires_auth(f): return decorated +UPLOAD_FOLDER = os.path.join(os.getcwd(), 'work_dir', 'uploads') + +@app.route('/upload', methods=['POST']) +async def upload_file(): + if 'file' not in request.files: + return jsonify({'ok': False, 'message': 'No file part'}), 400 + + files = request.files.getlist('file') # Handle multiple files + saved_filenames = [] + + for file in files: + if file and allowed_file(file.filename): # Check file type + filename = secure_filename(file.filename) + file.save(os.path.join(UPLOAD_FOLDER, filename)) + saved_filenames.append(filename) + + return jsonify({'ok': True, 'filenames': saved_filenames}) # Return saved filenames + + +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'txt', 'pdf', 'csv', 'html', 'json', 'md'} + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + + +@app.route("/import_knowledge", methods=["POST"]) +async def import_knowledge(): + if 'files[]' not in request.files: + return jsonify({'ok': False, 'message': 'No files part'}), 400 + + files = request.files.getlist('files[]') + KNOWLEDGE_FOLDER = os.path.join(os.getcwd(), 'knowledge', 'custom', 'main') + + saved_filenames = [] + + for file in files: + if file: + filename = secure_filename(file.filename) + file.save(os.path.join(KNOWLEDGE_FOLDER, filename)) + saved_filenames.append(filename) + + return jsonify({'ok': True, 'message': "Knowledge Imported", 'filenames': saved_filenames}) + + +@app.route("/work_dir", methods=["GET"]) # Correct route +async def browse_work_dir(): + work_dir = os.path.join(os.getcwd(), 'work_dir') + try: + files = [f for f in os.listdir(work_dir) if os.path.isfile(os.path.join(work_dir, f))] + return jsonify({'ok': True, 'files': files}) + except FileNotFoundError: + return jsonify({'ok': False, 'message': 'work_dir not found'}), 404 + except Exception as e: + return jsonify({'ok': False, 'message': f'Error browsing work_dir: {str(e)}'}), 500 + # handle default address, show demo html page from ./test_form.html @app.route("/", methods=["GET"]) async def test_form(): diff --git a/webui/index.css b/webui/index.css index c65bc8730..890117b3c 100644 --- a/webui/index.css +++ b/webui/index.css @@ -371,8 +371,8 @@ pre { display: flex; flex-direction: column; flex-grow: 1; - overflow-y: auto; - scroll-behavior: smooth; + overflow-y: scroll; + scroll-behavior: auto !important; /* avoid infinite scrolling! */ overflow-x: hidden; padding: var(--spacing-md) var(--spacing-md) 0; transition: all 0.3s ease; @@ -392,18 +392,15 @@ pre { } #chat-history::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); border-radius: 3px; } #chat-history::-webkit-scrollbar-thumb { border-radius: 3px; - -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); background-color: #555; transition: background-color var(--transition-speed) ease-in-out; - -webkit-transition: background-color var(--transition-speed) ease-in-out; } #chat-history::-webkit-scrollbar-thumb:hover { @@ -776,7 +773,7 @@ pre { display: flex; flex-direction: column; gap: var(--spacing-xs); - padding: var(--spacing-sm) var(--spacing-md); + padding: var(--spacing-sm) var(--spacing-md) var(--spacing-sm) 0.8rem; background-color: var(--color-panel); } @@ -832,6 +829,7 @@ pre { width: 100%; display: flex; padding-top: var(--spacing-xs); + margin-left: 0.7rem; gap: var(--spacing-xs); white-space: pre-wrap; } @@ -889,6 +887,7 @@ pre { background-color: var(--color-primary-light); } +/* MIC BUTTON */ #microphone-button { background-color: #3270e2; } @@ -896,6 +895,9 @@ pre { #microphone-button:hover { background-color: #4382e8; } +#microphone-button.recording { + background-color: #ff4136; /* Red color for recording */ +} .chat-button svg { width: 1.5rem; @@ -920,12 +922,12 @@ pre { /* Image preview section */ .image-preview-section { display: flex; - gap: 8px; - padding: 8px; + gap: var(--spacing-md); + padding: var(--spacing-sm) var(--spacing-sm) 0.37rem var(--spacing-sm); overflow-x: auto; background-color: var(--color-input); border-radius: 8px; - margin-bottom: 8px; + margin-bottom: var(--spacing-xs); } .preview-item { @@ -934,19 +936,18 @@ pre { } .preview-item img { - width: 100px; - height: 100px; + max-height: 100px; object-fit: cover; - border-radius: 4px; - border: 1px solid var(--color-border); + border-radius: 8px; + border: 1px solid var(--color-border-light); } .remove-image { position: absolute; top: -6px; right: -6px; - width: 20px; - height: 20px; + width: 18px; + height: 18px; border-radius: 50%; background-color: var(--color-accent); color: white; @@ -1047,7 +1048,8 @@ input:checked + .slider:before { cursor: pointer; font-size: var(--font-size-normal); height: 2.525rem; - margin-left: var(--spacing-sm); + margin-left: var(--spacing-xs); + margin-right: 0.18rem; transition: background-color var(--transition-speed), transform 0.1s ease-in-out; transition: background-color var(--transition-speed), transform 0.1s ease-in-out; -webkit-transition: background-color var(--transition-speed), transform 0.1s ease-in-out; diff --git a/webui/index.html b/webui/index.html index b14877aa7..95e076d2b 100644 --- a/webui/index.html +++ b/webui/index.html @@ -213,7 +213,7 @@ @@ -236,17 +236,24 @@ - + +
+