syntaxmatrix 2.6.0__tar.gz → 2.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/PKG-INFO +1 -1
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/SyntaxMatrix.egg-info/PKG-INFO +1 -1
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/SyntaxMatrix.egg-info/SOURCES.txt +2 -1
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/setup.py +1 -1
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/core.py +59 -7
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/db.py +46 -2
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/routes.py +187 -6
- syntaxmatrix-2.6.2/syntaxmatrix/templates/admin_branding.html +104 -0
- syntaxmatrix-2.6.2/syntaxmatrix/templates/admin_features.html +63 -0
- syntaxmatrix-2.6.0/syntaxmatrix/static/icons/logo2.png +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/LICENSE.txt +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/README.md +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/SyntaxMatrix.egg-info/dependency_links.txt +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/SyntaxMatrix.egg-info/requires.txt +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/SyntaxMatrix.egg-info/top_level.txt +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/pyproject.toml +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/setup.cfg +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/agent_tools.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/agents.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/agents_orchestrer.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/code_tools_registry.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/agentic/model_templates.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/auth.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/bootstrap.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/commentary.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/dataset_preprocessing.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/display_html.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/emailer.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/file_processor.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/gpt_models_latest.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/history_store.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/kernel_manager.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/llm_store.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/media/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/media/media_pixabay.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/models.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/page_builder_defaults.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/page_builder_generation.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/page_layout_contract.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/page_patch_publish.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/plottings.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/preface.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/profiles.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/project_root.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/selftest_page_templates.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/session.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/client_items.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/default.yaml +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/logging.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/model_map.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/prompts.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/settings/string_navbar.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/smiv.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/smpv.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/assets/hero-default.svg +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/css/style.css +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/docs.md +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/bot-icon.png +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/favicon.png +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/logo.png +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/logo3.png +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/svg_497526.svg +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/icons/svg_497528.svg +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/js/chat.js +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/js/sidebar.js +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/static/js/widgets.js +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/admin_secretes.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/change_password.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/code_cell.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/dashboard.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/dataset_resize.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/docs.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/edit_page.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/error.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/login.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/templates/register.html +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/themes.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/ui_modes.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/utils.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vector_db.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/adapters/__init__.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/adapters/milvus_adapter.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/adapters/pgvector_adapter.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/adapters/sqlite_adapter.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/base.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/registry.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectorizer.py +0 -0
- {syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/workspace_db.py +0 -0
|
@@ -67,13 +67,14 @@ syntaxmatrix/static/css/style.css
|
|
|
67
67
|
syntaxmatrix/static/icons/bot-icon.png
|
|
68
68
|
syntaxmatrix/static/icons/favicon.png
|
|
69
69
|
syntaxmatrix/static/icons/logo.png
|
|
70
|
-
syntaxmatrix/static/icons/logo2.png
|
|
71
70
|
syntaxmatrix/static/icons/logo3.png
|
|
72
71
|
syntaxmatrix/static/icons/svg_497526.svg
|
|
73
72
|
syntaxmatrix/static/icons/svg_497528.svg
|
|
74
73
|
syntaxmatrix/static/js/chat.js
|
|
75
74
|
syntaxmatrix/static/js/sidebar.js
|
|
76
75
|
syntaxmatrix/static/js/widgets.js
|
|
76
|
+
syntaxmatrix/templates/admin_branding.html
|
|
77
|
+
syntaxmatrix/templates/admin_features.html
|
|
77
78
|
syntaxmatrix/templates/admin_secretes.html
|
|
78
79
|
syntaxmatrix/templates/change_password.html
|
|
79
80
|
syntaxmatrix/templates/code_cell.html
|
|
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="syntaxmatrix",
|
|
11
|
-
version="2.6.
|
|
11
|
+
version="2.6.2",
|
|
12
12
|
author="Bob Nti",
|
|
13
13
|
author_email="bob.nti@syntaxmatrix.net",
|
|
14
14
|
description="SyntaxMUI: A customizable framework for Python AI Assistant Projects.",
|
|
@@ -40,6 +40,9 @@ _CLIENT_DIR = detect_project_root()
|
|
|
40
40
|
_HISTORY_DIR = os.path.join(_CLIENT_DIR, "smx_history")
|
|
41
41
|
os.makedirs(_HISTORY_DIR, exist_ok=True)
|
|
42
42
|
|
|
43
|
+
_BRANDING_DIR = os.path.join(_CLIENT_DIR, "branding")
|
|
44
|
+
os.makedirs(_BRANDING_DIR, exist_ok=True)
|
|
45
|
+
|
|
43
46
|
_SECRET_PATH = os.path.join(_CLIENT_DIR, ".smx_secret_key")
|
|
44
47
|
|
|
45
48
|
# OPENAI_API_KEY = getenv_api_key(_CLIENT_DIR, "OPENAI_API_KEY"))
|
|
@@ -72,6 +75,8 @@ class SyntaxMUI:
|
|
|
72
75
|
self.bot_icon = bot_icon
|
|
73
76
|
self.site_logo = site_logo
|
|
74
77
|
self.favicon = favicon
|
|
78
|
+
self._default_site_logo = site_logo
|
|
79
|
+
self._default_favicon = favicon
|
|
75
80
|
self.site_title = site_title
|
|
76
81
|
self.project_name = project_name
|
|
77
82
|
self.ui_mode = ui_mode
|
|
@@ -99,6 +104,9 @@ class SyntaxMUI:
|
|
|
99
104
|
self._last_llm_usage = None
|
|
100
105
|
routes.setup_routes(self)
|
|
101
106
|
|
|
107
|
+
# Apply client branding overrides if present on disk
|
|
108
|
+
self._apply_branding_from_disk()
|
|
109
|
+
|
|
102
110
|
# LLM Profiles
|
|
103
111
|
self.admin_profile = {}
|
|
104
112
|
self.chat_profile = {}
|
|
@@ -107,11 +115,12 @@ class SyntaxMUI:
|
|
|
107
115
|
self.coder_profile = {}
|
|
108
116
|
self.imagetexter_profile = {}
|
|
109
117
|
self.textimager_profile = {}
|
|
110
|
-
self.
|
|
118
|
+
self.imageeditor_profile = {}
|
|
111
119
|
|
|
112
120
|
self._gpt_models_latest_prev_resp_ids = {}
|
|
113
121
|
self.is_streaming = False
|
|
114
122
|
self.stream_args = {}
|
|
123
|
+
self._apply_feature_flags_from_db()
|
|
115
124
|
|
|
116
125
|
self._recent_visual_summaries = []
|
|
117
126
|
|
|
@@ -321,6 +330,24 @@ class SyntaxMUI:
|
|
|
321
330
|
|
|
322
331
|
def enable_registration(self):
|
|
323
332
|
self.registration_enabled = True
|
|
333
|
+
|
|
334
|
+
def _apply_feature_flags_from_db(self):
|
|
335
|
+
"""
|
|
336
|
+
Pull persisted toggles from app_settings.
|
|
337
|
+
"""
|
|
338
|
+
def _truthy(v):
|
|
339
|
+
return str(v or "").strip().lower() in ("1", "true", "yes", "on")
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
stream_v = db.get_setting("feature.stream_mode", "0")
|
|
343
|
+
user_files_v = db.get_setting("feature.user_files", "0")
|
|
344
|
+
|
|
345
|
+
self.is_streaming = _truthy(stream_v)
|
|
346
|
+
self.user_files_enabled = _truthy(user_files_v)
|
|
347
|
+
except Exception:
|
|
348
|
+
# Keep defaults if DB isn't ready for any reason
|
|
349
|
+
pass
|
|
350
|
+
|
|
324
351
|
|
|
325
352
|
@staticmethod
|
|
326
353
|
def columns(components):
|
|
@@ -330,24 +357,49 @@ class SyntaxMUI:
|
|
|
330
357
|
col_html += "</div>"
|
|
331
358
|
return col_html
|
|
332
359
|
|
|
360
|
+
# Site Branding
|
|
333
361
|
def set_site_title(self, title):
|
|
334
362
|
self.site_title = title
|
|
335
|
-
|
|
336
363
|
def set_project_name(self, project_name):
|
|
337
364
|
self.project_name = project_name
|
|
338
|
-
|
|
339
365
|
def set_favicon(self, icon):
|
|
340
366
|
self.favicon = icon
|
|
341
|
-
|
|
342
|
-
|
|
343
367
|
def set_site_logo(self, logo):
|
|
344
368
|
self.site_logo = logo
|
|
345
|
-
|
|
346
369
|
def set_user_icon(self, icon):
|
|
347
370
|
self.user_icon = icon
|
|
348
|
-
|
|
349
371
|
def set_bot_icon(self, icon):
|
|
350
372
|
self.bot_icon = icon
|
|
373
|
+
|
|
374
|
+
def _apply_branding_from_disk(self):
|
|
375
|
+
"""
|
|
376
|
+
If a client logo/favicon exists in syntaxmatrixdir/branding/,
|
|
377
|
+
use it; otherwise keep the framework defaults.
|
|
378
|
+
"""
|
|
379
|
+
branding_dir = os.path.join(_CLIENT_DIR, "branding")
|
|
380
|
+
|
|
381
|
+
def _pick(basename: str):
|
|
382
|
+
for ext in (".png", ".jpg", ".jpeg"):
|
|
383
|
+
fn = f"{basename}{ext}"
|
|
384
|
+
p = os.path.join(branding_dir, fn)
|
|
385
|
+
if os.path.exists(p):
|
|
386
|
+
return fn
|
|
387
|
+
return None
|
|
388
|
+
|
|
389
|
+
logo_fn = _pick("logo")
|
|
390
|
+
fav_fn = _pick("favicon")
|
|
391
|
+
|
|
392
|
+
if logo_fn:
|
|
393
|
+
# Use client-served endpoint (added in routes.py below)
|
|
394
|
+
self.site_logo = f"<img src='/branding/{logo_fn}' width='45' alt='logo'/>"
|
|
395
|
+
else:
|
|
396
|
+
self.site_logo = getattr(self, "_default_site_logo", self.site_logo)
|
|
397
|
+
|
|
398
|
+
if fav_fn:
|
|
399
|
+
self.favicon = f"/branding/{fav_fn}"
|
|
400
|
+
else:
|
|
401
|
+
self.favicon = getattr(self, "_default_favicon", self.favicon)
|
|
402
|
+
|
|
351
403
|
|
|
352
404
|
def text_input(self, key, id, label, placeholder=""):
|
|
353
405
|
if not placeholder:
|
|
@@ -68,6 +68,14 @@ def init_db():
|
|
|
68
68
|
)
|
|
69
69
|
""")
|
|
70
70
|
|
|
71
|
+
conn.execute("""
|
|
72
|
+
CREATE TABLE IF NOT EXISTS app_settings (
|
|
73
|
+
key TEXT PRIMARY KEY,
|
|
74
|
+
value TEXT NOT NULL,
|
|
75
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
76
|
+
)
|
|
77
|
+
""")
|
|
78
|
+
|
|
71
79
|
conn.commit()
|
|
72
80
|
conn.close()
|
|
73
81
|
|
|
@@ -447,7 +455,7 @@ def get_page_layout(page_name: str) -> dict | None:
|
|
|
447
455
|
|
|
448
456
|
|
|
449
457
|
def _init_media_assets_table() -> None:
|
|
450
|
-
|
|
458
|
+
|
|
451
459
|
conn = sqlite3.connect(DB_PATH)
|
|
452
460
|
try:
|
|
453
461
|
with conn:
|
|
@@ -495,7 +503,7 @@ def upsert_media_asset(
|
|
|
495
503
|
if not rel_path:
|
|
496
504
|
return
|
|
497
505
|
_init_media_assets_table()
|
|
498
|
-
|
|
506
|
+
|
|
499
507
|
conn = sqlite3.connect(DB_PATH)
|
|
500
508
|
try:
|
|
501
509
|
with conn:
|
|
@@ -564,3 +572,39 @@ def get_media_asset_by_rel_path(rel_path: str) -> dict | None:
|
|
|
564
572
|
}
|
|
565
573
|
finally:
|
|
566
574
|
conn.close()
|
|
575
|
+
|
|
576
|
+
# ***************************************
|
|
577
|
+
# App Settings Helpers (feature toggles)
|
|
578
|
+
# ***************************************
|
|
579
|
+
def set_setting(key: str, value: str) -> None:
|
|
580
|
+
if not key:
|
|
581
|
+
return
|
|
582
|
+
key = key.strip()
|
|
583
|
+
value = "" if value is None else str(value)
|
|
584
|
+
conn = sqlite3.connect(DB_PATH)
|
|
585
|
+
try:
|
|
586
|
+
with conn:
|
|
587
|
+
conn.execute(
|
|
588
|
+
"""
|
|
589
|
+
INSERT INTO app_settings (key, value, updated_at)
|
|
590
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
591
|
+
ON CONFLICT(key) DO UPDATE SET
|
|
592
|
+
value = excluded.value,
|
|
593
|
+
updated_at = CURRENT_TIMESTAMP
|
|
594
|
+
""",
|
|
595
|
+
(key, value),
|
|
596
|
+
)
|
|
597
|
+
finally:
|
|
598
|
+
conn.close()
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def get_setting(key: str, default: str | None = None) -> str | None:
|
|
602
|
+
if not key:
|
|
603
|
+
return default
|
|
604
|
+
key = key.strip()
|
|
605
|
+
conn = sqlite3.connect(DB_PATH)
|
|
606
|
+
try:
|
|
607
|
+
row = conn.execute("SELECT value FROM app_settings WHERE key = ?", (key,)).fetchone()
|
|
608
|
+
return row[0] if row else default
|
|
609
|
+
finally:
|
|
610
|
+
conn.close()
|
|
@@ -3326,6 +3326,10 @@ def setup_routes(smx):
|
|
|
3326
3326
|
def upload_user_file():
|
|
3327
3327
|
import uuid
|
|
3328
3328
|
from flask import jsonify
|
|
3329
|
+
|
|
3330
|
+
if not getattr(smx, "user_files_enabled", False):
|
|
3331
|
+
return jsonify({"error": "user_files_disabled"}), 403
|
|
3332
|
+
|
|
3329
3333
|
# Define the upload folder for user files.
|
|
3330
3334
|
upload_folder = os.path.join(_CLIENT_DIR, "uploads", "user")
|
|
3331
3335
|
if not os.path.exists(upload_folder):
|
|
@@ -4501,15 +4505,21 @@ def setup_routes(smx):
|
|
|
4501
4505
|
<ul class="catalog-list" style="padding-left:1rem; margin-bottom:0;">
|
|
4502
4506
|
{profile_items or "<li class='li-row'>No profiles yet.</li>"}
|
|
4503
4507
|
</ul>
|
|
4508
|
+
|
|
4509
|
+
<!-- Refresh button (reload admin page; anchor back to Models section) -->
|
|
4510
|
+
<div style="display:flex; justify-content:flex-end; margin-top:10px;">
|
|
4511
|
+
<a class="btn" href="/admin?refresh=profiles#models" title="Reload to refresh profiles list">Refresh</a>
|
|
4512
|
+
</div>
|
|
4504
4513
|
</div>
|
|
4505
4514
|
"""
|
|
4506
4515
|
|
|
4516
|
+
|
|
4507
4517
|
# ────────────────────────────────────────────────────────────────────────────────
|
|
4508
4518
|
# SYSTEM FILES
|
|
4509
4519
|
# ────────────────────────────────────────────────────────────────────────────────
|
|
4510
4520
|
sys_files_card = f"""
|
|
4511
|
-
<div class="card span-
|
|
4512
|
-
<h4>Upload System Files
|
|
4521
|
+
<div class="card span-3">
|
|
4522
|
+
<h4>Upload System Files<br>(PDFs only)</h4>
|
|
4513
4523
|
<form id="form-upload" method="post" enctype="multipart/form-data" style="display:inline-block;">
|
|
4514
4524
|
<input type="file" name="upload_files" accept=".pdf" multiple>
|
|
4515
4525
|
<button type="submit" name="action" value="upload_files">Upload</button>
|
|
@@ -4540,7 +4550,7 @@ def setup_routes(smx):
|
|
|
4540
4550
|
"""
|
|
4541
4551
|
|
|
4542
4552
|
manage_sys_files_card = f"""
|
|
4543
|
-
<div class='card span-
|
|
4553
|
+
<div class='card span-3'>
|
|
4544
4554
|
<h4>Manage Company Files</h4>
|
|
4545
4555
|
<ul class="catalog-list" style="list-style:none; padding-left:0; margin:0;">
|
|
4546
4556
|
{sys_files_html or "<li>No company file has been uploaded yet.</li>"}
|
|
@@ -4964,7 +4974,7 @@ def setup_routes(smx):
|
|
|
4964
4974
|
pixabay_saved = bool(os.environ.get("PIXABAY_API_KEY"))
|
|
4965
4975
|
|
|
4966
4976
|
secretes_link_card = f"""
|
|
4967
|
-
<div class="card span-
|
|
4977
|
+
<div class="card span-3">
|
|
4968
4978
|
<h4>Integrations (Secrets)</h4>
|
|
4969
4979
|
<div style="font-size:.72rem;color:#555;margin-top:-6px;margin-bottom:10px;line-height:1.35;">
|
|
4970
4980
|
Store secrete credentials.
|
|
@@ -4973,17 +4983,39 @@ def setup_routes(smx):
|
|
|
4973
4983
|
</div>
|
|
4974
4984
|
"""
|
|
4975
4985
|
|
|
4986
|
+
features_link_card = f"""
|
|
4987
|
+
<div class="card span-4">
|
|
4988
|
+
<h4>Feature toggles</h4>
|
|
4989
|
+
<div style="font-size:.72rem;color:#555;margin-top:-6px;margin-bottom:10px;line-height:1.35;">
|
|
4990
|
+
Turn streaming on/off and allow user file uploads in chat.
|
|
4991
|
+
</div>
|
|
4992
|
+
<a href="{url_for('admin_features')}" class="btn">Manage features</a>
|
|
4993
|
+
</div>
|
|
4994
|
+
"""
|
|
4995
|
+
|
|
4996
|
+
branding_link_card = f"""
|
|
4997
|
+
<div class="card span-3">
|
|
4998
|
+
<h4>Branding</h4>
|
|
4999
|
+
<div style="font-size:.72rem;color:#555;margin-top:-6px;margin-bottom:10px;line-height:1.35;">
|
|
5000
|
+
Upload your company logo and favicon (PNG/JPG). Defaults are used if nothing is uploaded.
|
|
5001
|
+
</div>
|
|
5002
|
+
<a href="{url_for('admin_branding')}" class="btn">Manage branding</a>
|
|
5003
|
+
</div>
|
|
5004
|
+
"""
|
|
5005
|
+
|
|
4976
5006
|
system_section = f"""
|
|
4977
5007
|
<section id="system" class="section">
|
|
4978
5008
|
<h2>System</h2>
|
|
4979
5009
|
<div class="admin-grid">
|
|
4980
5010
|
{secretes_link_card}
|
|
5011
|
+
{branding_link_card}
|
|
5012
|
+
{features_link_card}
|
|
4981
5013
|
{sys_files_card}
|
|
4982
5014
|
{manage_sys_files_card}
|
|
4983
5015
|
</div>
|
|
5016
|
+
|
|
4984
5017
|
</section>
|
|
4985
5018
|
"""
|
|
4986
|
-
|
|
4987
5019
|
users_section = f"""
|
|
4988
5020
|
<section id="users" class="section">
|
|
4989
5021
|
<h2>Users</h2>
|
|
@@ -5518,6 +5550,148 @@ def setup_routes(smx):
|
|
|
5518
5550
|
return render_template("admin_secretes.html", secret_names=names)
|
|
5519
5551
|
|
|
5520
5552
|
|
|
5553
|
+
@smx.app.route("/admin/branding", methods=["GET", "POST"])
|
|
5554
|
+
@admin_required
|
|
5555
|
+
def admin_branding():
|
|
5556
|
+
branding_dir = os.path.join(_CLIENT_DIR, "branding")
|
|
5557
|
+
os.makedirs(branding_dir, exist_ok=True)
|
|
5558
|
+
|
|
5559
|
+
allowed_ext = {".png", ".jpg", ".jpeg"}
|
|
5560
|
+
max_logo_bytes = 5 * 1024 * 1024 # 5 MB
|
|
5561
|
+
max_favicon_bytes = 1 * 1024 * 1024 # 1 MB
|
|
5562
|
+
|
|
5563
|
+
def _find(base: str):
|
|
5564
|
+
for ext in (".png", ".jpg", ".jpeg"):
|
|
5565
|
+
p = os.path.join(branding_dir, f"{base}{ext}")
|
|
5566
|
+
if os.path.exists(p):
|
|
5567
|
+
return f"{base}{ext}"
|
|
5568
|
+
return None
|
|
5569
|
+
|
|
5570
|
+
def _delete_all(base: str):
|
|
5571
|
+
for ext in (".png", ".jpg", ".jpeg"):
|
|
5572
|
+
p = os.path.join(branding_dir, f"{base}{ext}")
|
|
5573
|
+
if os.path.exists(p):
|
|
5574
|
+
try:
|
|
5575
|
+
os.remove(p)
|
|
5576
|
+
except Exception:
|
|
5577
|
+
pass
|
|
5578
|
+
|
|
5579
|
+
def _save_upload(field_name: str, base: str, max_bytes: int):
|
|
5580
|
+
f = request.files.get(field_name)
|
|
5581
|
+
if not f or not f.filename:
|
|
5582
|
+
return False, None
|
|
5583
|
+
|
|
5584
|
+
ext = os.path.splitext(f.filename.lower())[1].strip()
|
|
5585
|
+
if ext not in allowed_ext:
|
|
5586
|
+
return False, f"Invalid file type for {base}. Use PNG or JPG."
|
|
5587
|
+
|
|
5588
|
+
# size check
|
|
5589
|
+
try:
|
|
5590
|
+
f.stream.seek(0, os.SEEK_END)
|
|
5591
|
+
size = f.stream.tell()
|
|
5592
|
+
f.stream.seek(0)
|
|
5593
|
+
except Exception:
|
|
5594
|
+
size = None
|
|
5595
|
+
|
|
5596
|
+
if size is not None and size > max_bytes:
|
|
5597
|
+
return False, f"{base.capitalize()} is too large. Max {max_bytes // (1024*1024)} MB."
|
|
5598
|
+
|
|
5599
|
+
# Replace existing logo.* / favicon.*
|
|
5600
|
+
_delete_all(base)
|
|
5601
|
+
|
|
5602
|
+
out_path = os.path.join(branding_dir, f"{base}{ext}")
|
|
5603
|
+
try:
|
|
5604
|
+
f.save(out_path)
|
|
5605
|
+
except Exception as e:
|
|
5606
|
+
return False, f"Failed to save {base}: {e}"
|
|
5607
|
+
|
|
5608
|
+
return True, None
|
|
5609
|
+
|
|
5610
|
+
# POST actions
|
|
5611
|
+
if request.method == "POST":
|
|
5612
|
+
action = (request.form.get("action") or "upload").strip().lower()
|
|
5613
|
+
|
|
5614
|
+
if action == "reset":
|
|
5615
|
+
_delete_all("logo")
|
|
5616
|
+
_delete_all("favicon")
|
|
5617
|
+
try:
|
|
5618
|
+
smx._apply_branding_from_disk()
|
|
5619
|
+
except Exception:
|
|
5620
|
+
pass
|
|
5621
|
+
flash("Branding reset to defaults ✓")
|
|
5622
|
+
return redirect(url_for("admin_branding"))
|
|
5623
|
+
|
|
5624
|
+
ok1, err1 = _save_upload("logo_file", "logo", max_logo_bytes)
|
|
5625
|
+
ok2, err2 = _save_upload("favicon_file", "favicon", max_favicon_bytes)
|
|
5626
|
+
|
|
5627
|
+
if err1:
|
|
5628
|
+
flash(err1, "error")
|
|
5629
|
+
if err2:
|
|
5630
|
+
flash(err2, "error")
|
|
5631
|
+
|
|
5632
|
+
if ok1 or ok2:
|
|
5633
|
+
try:
|
|
5634
|
+
smx._apply_branding_from_disk()
|
|
5635
|
+
except Exception:
|
|
5636
|
+
pass
|
|
5637
|
+
flash("Branding updated ✓")
|
|
5638
|
+
|
|
5639
|
+
return redirect(url_for("admin_branding"))
|
|
5640
|
+
|
|
5641
|
+
# GET: show current status
|
|
5642
|
+
logo_fn = _find("logo")
|
|
5643
|
+
fav_fn = _find("favicon")
|
|
5644
|
+
|
|
5645
|
+
cache_bust = int(time.time())
|
|
5646
|
+
|
|
5647
|
+
logo_url = f"/branding/{logo_fn}?v={cache_bust}" if logo_fn else None
|
|
5648
|
+
favicon_url = f"/branding/{fav_fn}?v={cache_bust}" if fav_fn else None
|
|
5649
|
+
|
|
5650
|
+
default_logo_html = getattr(smx, "_default_site_logo", smx.site_logo)
|
|
5651
|
+
default_favicon_url = getattr(smx, "_default_favicon", smx.favicon)
|
|
5652
|
+
|
|
5653
|
+
return render_template(
|
|
5654
|
+
"admin_branding.html",
|
|
5655
|
+
logo_url=logo_url,
|
|
5656
|
+
favicon_url=favicon_url,
|
|
5657
|
+
default_logo_html=Markup(default_logo_html),
|
|
5658
|
+
default_favicon_url=default_favicon_url,
|
|
5659
|
+
)
|
|
5660
|
+
|
|
5661
|
+
|
|
5662
|
+
@smx.app.route("/admin/features", methods=["GET", "POST"])
|
|
5663
|
+
@admin_required
|
|
5664
|
+
def admin_features():
|
|
5665
|
+
# Defaults from DB (or fall back)
|
|
5666
|
+
def _truthy(v):
|
|
5667
|
+
return str(v or "").strip().lower() in ("1", "true", "yes", "on")
|
|
5668
|
+
|
|
5669
|
+
if request.method == "POST":
|
|
5670
|
+
stream_on = "1" if request.form.get("stream_mode") == "on" else "0"
|
|
5671
|
+
user_files_on = "1" if request.form.get("user_files") == "on" else "0"
|
|
5672
|
+
|
|
5673
|
+
db.set_setting("feature.stream_mode", stream_on)
|
|
5674
|
+
db.set_setting("feature.user_files", user_files_on)
|
|
5675
|
+
|
|
5676
|
+
# Apply immediately (no restart)
|
|
5677
|
+
try:
|
|
5678
|
+
smx._apply_feature_flags_from_db()
|
|
5679
|
+
except Exception:
|
|
5680
|
+
pass
|
|
5681
|
+
|
|
5682
|
+
flash("Settings updated ✓")
|
|
5683
|
+
return redirect(url_for("admin_features"))
|
|
5684
|
+
|
|
5685
|
+
stream_mode = _truthy(db.get_setting("feature.stream_mode", "0"))
|
|
5686
|
+
user_files = _truthy(db.get_setting("feature.user_files", "0"))
|
|
5687
|
+
|
|
5688
|
+
return render_template(
|
|
5689
|
+
"admin_features.html",
|
|
5690
|
+
stream_mode=stream_mode,
|
|
5691
|
+
user_files=user_files,
|
|
5692
|
+
)
|
|
5693
|
+
|
|
5694
|
+
|
|
5521
5695
|
@smx.app.route("/admin/delete.json", methods=["POST"])
|
|
5522
5696
|
def admin_delete_universal():
|
|
5523
5697
|
|
|
@@ -6407,13 +6581,20 @@ def setup_routes(smx):
|
|
|
6407
6581
|
|
|
6408
6582
|
return jsonify({"file_paths": file_paths})
|
|
6409
6583
|
|
|
6410
|
-
|
|
6584
|
+
|
|
6411
6585
|
# Serve the raw media files
|
|
6412
6586
|
@smx.app.route('/uploads/media/<path:filename>')
|
|
6413
6587
|
def serve_media(filename):
|
|
6414
6588
|
media_dir = os.path.join(_CLIENT_DIR, 'uploads', 'media')
|
|
6415
6589
|
return send_from_directory(media_dir, filename)
|
|
6416
6590
|
|
|
6591
|
+
|
|
6592
|
+
@smx.app.route("/branding/<path:filename>")
|
|
6593
|
+
def serve_branding(filename):
|
|
6594
|
+
branding_dir = os.path.join(_CLIENT_DIR, "branding")
|
|
6595
|
+
return send_from_directory(branding_dir, filename)
|
|
6596
|
+
|
|
6597
|
+
|
|
6417
6598
|
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
6418
6599
|
# DASHBOARD
|
|
6419
6600
|
# ────────────────────────────────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Branding</title>
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;margin:0;background:#0b1224;color:#e5e7eb;}
|
|
9
|
+
.wrap{max-width:980px;margin:0 auto;padding:18px;}
|
|
10
|
+
.top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px;}
|
|
11
|
+
.top h1{margin:0;font-size:1.1rem;}
|
|
12
|
+
.btn{display:inline-block;padding:8px 12px;border-radius:999px;border:1px solid rgba(148,163,184,.35);color:#e5e7eb;text-decoration:none;background:transparent;cursor:pointer;font-weight:650;font-size:.85rem;}
|
|
13
|
+
.btn:hover{border-color:rgba(56,189,248,.6);color:#38bdf8;}
|
|
14
|
+
.card{background:rgba(2,6,23,.65);border:1px solid rgba(148,163,184,.25);border-radius:14px;padding:14px;box-shadow:0 18px 36px rgba(15,23,42,.6);margin-bottom:12px;}
|
|
15
|
+
label{display:block;font-size:.78rem;font-weight:750;margin-bottom:6px;color:#d1d5db;}
|
|
16
|
+
input[type="file"]{width:100%;padding:10px 11px;border-radius:12px;border:1px solid rgba(148,163,184,.25);background:rgba(2,6,23,.8);color:#e5e7eb;outline:none;}
|
|
17
|
+
.row{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
|
|
18
|
+
@media (max-width:760px){.row{grid-template-columns:1fr;}}
|
|
19
|
+
.hint{color:#9ca3af;font-size:.78rem;line-height:1.35;margin-top:8px;}
|
|
20
|
+
.flash{background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.35);padding:10px;border-radius:12px;margin-bottom:10px;color:#bbf7d0;font-size:.85rem;}
|
|
21
|
+
.flash.warn{background:rgba(239,68,68,.10);border-color:rgba(239,68,68,.30);color:#fecaca;}
|
|
22
|
+
.preview{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-top:10px;}
|
|
23
|
+
.chip{padding:6px 10px;border-radius:999px;border:1px solid rgba(148,163,184,.25);font-size:.8rem;color:#cbd5e1;}
|
|
24
|
+
.danger{border-color:rgba(239,68,68,.55);color:#fecaca;}
|
|
25
|
+
.danger:hover{border-color:rgba(239,68,68,.9);color:#fff;}
|
|
26
|
+
</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div class="wrap">
|
|
30
|
+
<div class="top">
|
|
31
|
+
<h1>Branding</h1>
|
|
32
|
+
<a class="btn" href="{{ url_for('admin_panel') }}#system">Back to Admin</a>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{% with messages = get_flashed_messages(with_categories=True) %}
|
|
36
|
+
{% if messages %}
|
|
37
|
+
{% for cat, m in messages %}
|
|
38
|
+
<div class="flash {{ 'warn' if cat == 'error' else '' }}">{{ m }}</div>
|
|
39
|
+
{% endfor %}
|
|
40
|
+
{% endif %}
|
|
41
|
+
{% endwith %}
|
|
42
|
+
|
|
43
|
+
<div class="card">
|
|
44
|
+
<h2 style="margin:0 0 10px;font-size:.95rem;">Upload logo and favicon</h2>
|
|
45
|
+
|
|
46
|
+
<form method="post" enctype="multipart/form-data">
|
|
47
|
+
<input type="hidden" name="action" value="upload">
|
|
48
|
+
|
|
49
|
+
<div class="row">
|
|
50
|
+
<div>
|
|
51
|
+
<label>Logo (PNG/JPG)</label>
|
|
52
|
+
<input type="file" name="logo_file" accept=".png,.jpg,.jpeg,image/png,image/jpeg">
|
|
53
|
+
<div class="hint">Tip: a wide logo works best. Up to 5 MB.</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div>
|
|
57
|
+
<label>Favicon (PNG/JPG)</label>
|
|
58
|
+
<input type="file" name="favicon_file" accept=".png,.jpg,.jpeg,image/png,image/jpeg">
|
|
59
|
+
<div class="hint">Tip: square image (32×32 or 48×48). Up to 1 MB.</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div style="margin-top:12px;display:flex;justify-content:flex-end;gap:10px;">
|
|
64
|
+
<button class="btn" type="submit">Save</button>
|
|
65
|
+
</div>
|
|
66
|
+
</form>
|
|
67
|
+
|
|
68
|
+
<div class="preview">
|
|
69
|
+
<div>
|
|
70
|
+
<div class="chip">Current logo</div>
|
|
71
|
+
<div style="margin-top:8px;">
|
|
72
|
+
{% if logo_url %}
|
|
73
|
+
<img src="{{ logo_url }}" alt="logo" style="max-height:44px;max-width:240px;">
|
|
74
|
+
{% else %}
|
|
75
|
+
{{ default_logo_html|safe }}
|
|
76
|
+
{% endif %}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div>
|
|
81
|
+
<div class="chip">Current favicon</div>
|
|
82
|
+
<div style="margin-top:8px;">
|
|
83
|
+
{% if favicon_url %}
|
|
84
|
+
<img src="{{ favicon_url }}" alt="favicon" style="width:32px;height:32px;border-radius:6px;">
|
|
85
|
+
{% else %}
|
|
86
|
+
<img src="{{ default_favicon_url }}" alt="favicon" style="width:32px;height:32px;border-radius:6px;">
|
|
87
|
+
{% endif %}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="card">
|
|
94
|
+
<h2 style="margin:0 0 10px;font-size:.95rem;">Reset</h2>
|
|
95
|
+
<div class="hint">Removes the uploaded logo/favicon so the app falls back to the SyntaxMatrix defaults.</div>
|
|
96
|
+
|
|
97
|
+
<form method="post" onsubmit="return confirm('Reset branding to defaults?');" style="margin-top:10px;">
|
|
98
|
+
<input type="hidden" name="action" value="reset">
|
|
99
|
+
<button class="btn danger" type="submit">Reset to defaults</button>
|
|
100
|
+
</form>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</body>
|
|
104
|
+
</html>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Feature toggles</title>
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;margin:0;background:#0b1224;color:#e5e7eb;}
|
|
9
|
+
.wrap{max-width:980px;margin:0 auto;padding:18px;}
|
|
10
|
+
.top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px;}
|
|
11
|
+
.top h1{margin:0;font-size:1.1rem;}
|
|
12
|
+
.btn{display:inline-block;padding:8px 12px;border-radius:999px;border:1px solid rgba(148,163,184,.35);color:#e5e7eb;text-decoration:none;background:transparent;cursor:pointer;font-weight:650;font-size:.85rem;}
|
|
13
|
+
.btn:hover{border-color:rgba(56,189,248,.6);color:#38bdf8;}
|
|
14
|
+
.card{background:rgba(2,6,23,.65);border:1px solid rgba(148,163,184,.25);border-radius:14px;padding:14px;box-shadow:0 18px 36px rgba(15,23,42,.6);margin-bottom:12px;}
|
|
15
|
+
.flash{background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.35);padding:10px;border-radius:12px;margin-bottom:10px;color:#bbf7d0;font-size:.85rem;}
|
|
16
|
+
.hint{color:#9ca3af;font-size:.78rem;line-height:1.35;margin-top:8px;}
|
|
17
|
+
.row{display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-top:1px solid rgba(148,163,184,.15);}
|
|
18
|
+
.row:first-child{border-top:none;}
|
|
19
|
+
.label{font-weight:750;}
|
|
20
|
+
input[type="checkbox"]{transform: translateY(2px); width:16px; height:16px;}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<div class="wrap">
|
|
25
|
+
<div class="top">
|
|
26
|
+
<h1>Feature toggles</h1>
|
|
27
|
+
<a class="btn" href="{{ url_for('admin_panel') }}#system">Back to Admin</a>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
{% with messages = get_flashed_messages(with_categories=False) %}
|
|
31
|
+
{% if messages %}
|
|
32
|
+
{% for m in messages %}
|
|
33
|
+
<div class="flash">{{ m }}</div>
|
|
34
|
+
{% endfor %}
|
|
35
|
+
{% endif %}
|
|
36
|
+
{% endwith %}
|
|
37
|
+
|
|
38
|
+
<div class="card">
|
|
39
|
+
<form method="post">
|
|
40
|
+
<div class="row">
|
|
41
|
+
<input type="checkbox" id="stream_mode" name="stream_mode" {% if stream_mode %}checked{% endif %}>
|
|
42
|
+
<div>
|
|
43
|
+
<div class="label">Enable stream mode</div>
|
|
44
|
+
<div class="hint">Streams assistant responses in real time (SSE). Applies to the chat UI immediately.</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="row">
|
|
49
|
+
<input type="checkbox" id="user_files" name="user_files" {% if user_files %}checked{% endif %}>
|
|
50
|
+
<div>
|
|
51
|
+
<div class="label">Enable user files</div>
|
|
52
|
+
<div class="hint">Shows the “➕” upload icon and allows users to upload PDFs for that chat session.</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div style="margin-top:12px;display:flex;justify-content:flex-end;gap:10px;">
|
|
57
|
+
<button class="btn" type="submit">Save</button>
|
|
58
|
+
</div>
|
|
59
|
+
</form>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{syntaxmatrix-2.6.0 → syntaxmatrix-2.6.2}/syntaxmatrix/vectordb/adapters/pgvector_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|