syntaxmatrix 2.5.5.5__py3-none-any.whl → 2.6.2__py3-none-any.whl

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.
Files changed (44) hide show
  1. syntaxmatrix/__init__.py +3 -2
  2. syntaxmatrix/agentic/agents.py +1220 -169
  3. syntaxmatrix/agentic/agents_orchestrer.py +326 -0
  4. syntaxmatrix/agentic/code_tools_registry.py +27 -32
  5. syntaxmatrix/auth.py +142 -5
  6. syntaxmatrix/commentary.py +16 -16
  7. syntaxmatrix/core.py +192 -84
  8. syntaxmatrix/db.py +460 -4
  9. syntaxmatrix/{display.py → display_html.py} +2 -6
  10. syntaxmatrix/gpt_models_latest.py +1 -1
  11. syntaxmatrix/media/__init__.py +0 -0
  12. syntaxmatrix/media/media_pixabay.py +277 -0
  13. syntaxmatrix/models.py +1 -1
  14. syntaxmatrix/page_builder_defaults.py +183 -0
  15. syntaxmatrix/page_builder_generation.py +1122 -0
  16. syntaxmatrix/page_layout_contract.py +644 -0
  17. syntaxmatrix/page_patch_publish.py +1471 -0
  18. syntaxmatrix/preface.py +670 -0
  19. syntaxmatrix/profiles.py +28 -10
  20. syntaxmatrix/routes.py +1941 -593
  21. syntaxmatrix/selftest_page_templates.py +360 -0
  22. syntaxmatrix/settings/client_items.py +28 -0
  23. syntaxmatrix/settings/model_map.py +1022 -207
  24. syntaxmatrix/settings/prompts.py +328 -130
  25. syntaxmatrix/static/assets/hero-default.svg +22 -0
  26. syntaxmatrix/static/icons/bot-icon.png +0 -0
  27. syntaxmatrix/static/icons/favicon.png +0 -0
  28. syntaxmatrix/static/icons/logo.png +0 -0
  29. syntaxmatrix/static/icons/logo3.png +0 -0
  30. syntaxmatrix/templates/admin_branding.html +104 -0
  31. syntaxmatrix/templates/admin_features.html +63 -0
  32. syntaxmatrix/templates/admin_secretes.html +108 -0
  33. syntaxmatrix/templates/change_password.html +124 -0
  34. syntaxmatrix/templates/dashboard.html +296 -131
  35. syntaxmatrix/templates/dataset_resize.html +535 -0
  36. syntaxmatrix/templates/edit_page.html +2535 -0
  37. syntaxmatrix/utils.py +2728 -2835
  38. {syntaxmatrix-2.5.5.5.dist-info → syntaxmatrix-2.6.2.dist-info}/METADATA +6 -2
  39. {syntaxmatrix-2.5.5.5.dist-info → syntaxmatrix-2.6.2.dist-info}/RECORD +42 -25
  40. syntaxmatrix/generate_page.py +0 -634
  41. syntaxmatrix/static/icons/hero_bg.jpg +0 -0
  42. {syntaxmatrix-2.5.5.5.dist-info → syntaxmatrix-2.6.2.dist-info}/WHEEL +0 -0
  43. {syntaxmatrix-2.5.5.5.dist-info → syntaxmatrix-2.6.2.dist-info}/licenses/LICENSE.txt +0 -0
  44. {syntaxmatrix-2.5.5.5.dist-info → syntaxmatrix-2.6.2.dist-info}/top_level.txt +0 -0
syntaxmatrix/core.py CHANGED
@@ -16,7 +16,6 @@ from .file_processor import process_admin_pdf_files
16
16
  from google.genai import types
17
17
  from .vector_db import query_embeddings
18
18
  from .vectorizer import embed_text
19
- from syntaxmatrix.settings.prompts import SMXAI_CHAT_ID, SMXAI_CHAT_INSTRUCTIONS, SMXAI_WEBSITE_DESCRIPTION
20
19
  from typing import List, Generator
21
20
  from .auth import init_auth_db
22
21
  from . import profiles as _prof
@@ -28,20 +27,26 @@ from html import unescape
28
27
  from .plottings import render_plotly, pyplot, describe_plotly, describe_matplotlib
29
28
  from threading import RLock
30
29
  from syntaxmatrix.settings.model_map import GPT_MODELS_LATEST
31
-
30
+ from syntaxmatrix.settings.prompts import(
31
+ SMXAI_CHAT_IDENTITY,
32
+ SMXAI_CHAT_INSTRUCTIONS,
33
+ SMXAI_WEBSITE_DESCRIPTION,
34
+ )
35
+ from syntaxmatrix.settings.client_items import read_client_file, getenv_api_key
32
36
 
33
37
  # ──────── framework‐local storage paths ────────
34
38
  # this ensures the key & data always live under the package dir,
35
- # regardless of where the developer `cd` into before launching.
36
39
  _CLIENT_DIR = detect_project_root()
37
40
  _HISTORY_DIR = os.path.join(_CLIENT_DIR, "smx_history")
38
41
  os.makedirs(_HISTORY_DIR, exist_ok=True)
39
42
 
43
+ _BRANDING_DIR = os.path.join(_CLIENT_DIR, "branding")
44
+ os.makedirs(_BRANDING_DIR, exist_ok=True)
45
+
40
46
  _SECRET_PATH = os.path.join(_CLIENT_DIR, ".smx_secret_key")
41
47
 
42
- _CLIENT_DOTENV_PATH = os.path.join(str(_CLIENT_DIR.parent), ".env")
43
- if os.path.isfile(_CLIENT_DOTENV_PATH):
44
- load_dotenv(_CLIENT_DOTENV_PATH, override=True)
48
+ # OPENAI_API_KEY = getenv_api_key(_CLIENT_DIR, "OPENAI_API_KEY"))
49
+ # dotenv_content = read_client_file(_CLIENT_DIR, ".env")
45
50
 
46
51
  _ICONS_PATH = os.path.join(_CLIENT_DIR, "static", "icons")
47
52
  os.makedirs(_ICONS_PATH, exist_ok=True)
@@ -53,9 +58,9 @@ class SyntaxMUI:
53
58
  host="127.0.0.1",
54
59
  port="5080",
55
60
  user_icon="👩🏿‍🦲",
56
- bot_icon="<img src='/static/icons/favicon.png' width=20' alt='bot'/>",
61
+ bot_icon="<img src='/static/icons/bot-icon.png' width=20' alt='bot'/>",
57
62
  favicon="/static/icons/favicon.png",
58
- site_logo="<img src='/static/icons/logo.png' width='30' alt='logo'/>",
63
+ site_logo="<img src='/static/icons/logo.png' width='45' alt='logo'/>",
59
64
  site_title="SyntaxMatrix",
60
65
  project_name="smxAI",
61
66
  theme_name="light",
@@ -70,12 +75,15 @@ class SyntaxMUI:
70
75
  self.bot_icon = bot_icon
71
76
  self.site_logo = site_logo
72
77
  self.favicon = favicon
78
+ self._default_site_logo = site_logo
79
+ self._default_favicon = favicon
73
80
  self.site_title = site_title
74
81
  self.project_name = project_name
75
82
  self.ui_mode = ui_mode
76
83
  self.theme_toggle_enabled = False
77
84
  self.user_files_enabled = False
78
- self.smxai_identity = SMXAI_CHAT_ID
85
+ self.registration_enabled = False
86
+ self.smxai_identity = SMXAI_CHAT_IDENTITY
79
87
  self.smxai_instructions = SMXAI_CHAT_INSTRUCTIONS
80
88
  self.website_description = SMXAI_WEBSITE_DESCRIPTION
81
89
  self._eda_output = {} # {chat_id: html}
@@ -96,15 +104,23 @@ class SyntaxMUI:
96
104
  self._last_llm_usage = None
97
105
  routes.setup_routes(self)
98
106
 
99
- self._admin_profile = {}
100
- self._chat_profile = {}
101
- self._coding_profile = {}
102
- self._classification_profile = {}
103
- self._summarization_profile = {}
107
+ # Apply client branding overrides if present on disk
108
+ self._apply_branding_from_disk()
109
+
110
+ # LLM Profiles
111
+ self.admin_profile = {}
112
+ self.chat_profile = {}
113
+ self.classifier_profile = {}
114
+ self.summarizer_profile = {}
115
+ self.coder_profile = {}
116
+ self.imagetexter_profile = {}
117
+ self.textimager_profile = {}
118
+ self.imageeditor_profile = {}
104
119
 
105
120
  self._gpt_models_latest_prev_resp_ids = {}
106
121
  self.is_streaming = False
107
122
  self.stream_args = {}
123
+ self._apply_feature_flags_from_db()
108
124
 
109
125
  self._recent_visual_summaries = []
110
126
 
@@ -311,6 +327,27 @@ class SyntaxMUI:
311
327
 
312
328
  def enable_user_files(self):
313
329
  self.user_files_enabled = True
330
+
331
+ def enable_registration(self):
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
+
314
351
 
315
352
  @staticmethod
316
353
  def columns(components):
@@ -320,23 +357,49 @@ class SyntaxMUI:
320
357
  col_html += "</div>"
321
358
  return col_html
322
359
 
360
+ # Site Branding
323
361
  def set_site_title(self, title):
324
362
  self.site_title = title
325
-
326
363
  def set_project_name(self, project_name):
327
364
  self.project_name = project_name
328
-
329
365
  def set_favicon(self, icon):
330
366
  self.favicon = icon
331
-
332
367
  def set_site_logo(self, logo):
333
368
  self.site_logo = logo
334
-
335
369
  def set_user_icon(self, icon):
336
370
  self.user_icon = icon
337
-
338
371
  def set_bot_icon(self, icon):
339
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
+
340
403
 
341
404
  def text_input(self, key, id, label, placeholder=""):
342
405
  if not placeholder:
@@ -514,12 +577,12 @@ class SyntaxMUI:
514
577
  # ──────────────────────────────────────────────────────────────
515
578
  # *********** LLM CLIENT HELPERS **********************
516
579
  # ──────────────────────────────────────────────────────────────
517
- def set_prompt_profile(self, profile):
518
- self.ai_chat_id = profile
580
+ def set_smxai_identity(self, profile):
581
+ self.set_smxai_identity = profile
519
582
 
520
583
 
521
- def set_prompt_instructions(self, instructions):
522
- self.ai_chat_instructions = instructions
584
+ def set_smxai_instructions(self, instructions):
585
+ self.set_smxai_instructions = instructions
523
586
 
524
587
 
525
588
  def set_website_description(self, desc):
@@ -592,18 +655,17 @@ class SyntaxMUI:
592
655
 
593
656
 
594
657
  def classify_query_intent(self, query: str) -> str:
595
- from syntaxmatrix.gpt_models_latest import extract_output_text as _out, set_args
596
658
 
597
- if not self._classification_profile:
598
- classification_profile = _prof.get_profile('classification') or _prof.get_profile('chat') or _prof.get_profile('admin')
599
- if not classification_profile:
600
- return {"Error": "Set a profile for Classification"}
601
- self._classification_profile = classification_profile
602
- self._classification_profile['client'] = _prof.get_client(classification_profile)
659
+ if not self.classifier_profile:
660
+ classifier_profile = _prof.get_profile('classifier') or _prof.get_profile('chat') or _prof.get_profile('summarizer') or _prof.get_profile('admin')
661
+ if not classifier_profile:
662
+ return "Error: Set a profile for Classification"
663
+ self.classifier_profile = classifier_profile
664
+ self.classifier_profile['client'] = _prof.get_client(classifier_profile)
603
665
 
604
- _client = self._classification_profile['client']
605
- _provider = self._classification_profile['provider']
606
- _model = self._classification_profile['model']
666
+ _client = self.classifier_profile['client']
667
+ _provider = self.classifier_profile['provider']
668
+ _model = self.classifier_profile['model']
607
669
 
608
670
  # New instruction format with hybrid option
609
671
  _intent_profile = "You are an intent classifier. Respond ONLY with the intent name."
@@ -697,35 +759,36 @@ class SyntaxMUI:
697
759
 
698
760
  def generate_contextual_title(self, chat_history):
699
761
 
700
- if not self._summarization_profile:
701
- summarization_profile = _prof.get_profile('summarization') or _prof.get_profile('chat') or _prof.get_profile('admin')
702
- if not summarization_profile:
703
- return {"Error": "Chat profile not set yet."}
762
+ if not self.summarizer_profile:
763
+ summarizer_profile = _prof.get_profile('summarizer') or _prof.get_profile('classifier') or _prof.get_profile('chat') or _prof.get_profile('admin')
764
+ if not summarizer_profile:
765
+ return "<code style='color:red;'>Error: No Agent setup yet.</code>"
704
766
 
705
- self._summarization_profile = summarization_profile
706
- self._summarization_profile['client'] = _prof.get_client(summarization_profile)
767
+ self.summarizer_profile = summarizer_profile
768
+ self.summarizer_profile['client'] = _prof.get_client(summarizer_profile)
707
769
 
708
770
  conversation = "\n".join([f"{role}: {msg}" for role, msg in chat_history])
709
771
  _title_profile = "You are a title generator that creates concise and relevant titles for the given conversations."
772
+
710
773
  _instructions = f"""
711
774
  Generate a contextual title (5 short words max) from the given Conversation History
712
775
  The title should be concise - with no preamble, relevant, and capture the essence of this Conversation: \n{conversation}.\n\n
713
776
  return only the title.
714
777
  """
715
778
 
716
- _client = self._summarization_profile['client']
717
- _provider = self._summarization_profile['provider']
718
- _model = self._summarization_profile['model']
779
+ _client = self.summarizer_profile['client']
780
+ _provider = self.summarizer_profile['provider']
781
+ _model = self.summarizer_profile['model']
719
782
 
720
783
  def google_generated_title():
721
784
  try:
722
785
  response = _client.models.generate_content(
723
786
  model=_model,
724
- contents=f"{_title_profile}\n{_instructions}"
787
+ contents=f"{_title_profile}\n\n{_instructions}"
725
788
  )
726
789
  return response.text.strip()
727
790
  except Exception as e:
728
- return f"Summary agent error!"
791
+ return f"Google Summary agent error!"
729
792
 
730
793
  def gpt_models_latest_generated_title():
731
794
  try:
@@ -740,7 +803,7 @@ class SyntaxMUI:
740
803
  resp = _client.responses.create(**args)
741
804
  return _out(resp).strip()
742
805
  except Exception as e:
743
- return f"Summary agent error!"
806
+ return f"OpenAI 5s Summary agent error!"
744
807
 
745
808
  def anthropic_generated_title():
746
809
  try:
@@ -753,7 +816,7 @@ class SyntaxMUI:
753
816
  )
754
817
  return response.content[0].text.strip()
755
818
  except Exception as e:
756
- return f"Summary agent error!"
819
+ return f"Anthropic Summary agent error!"
757
820
 
758
821
  def openai_sdk_generated_title():
759
822
  prompt = [
@@ -767,10 +830,14 @@ class SyntaxMUI:
767
830
  temperature=0.3,
768
831
  max_tokens=50
769
832
  )
770
- title = response.choices[0].message.content.strip().lower()
771
- return title if title else ""
833
+ print("\nRESPONSE:\n", response)
834
+
835
+ title = response.choices[0].message.content
836
+
837
+ print("\nTITLE:\n", title)
838
+ return title
772
839
  except Exception as e:
773
- return f"Summary agent error!"
840
+ return f"SDK Summary agent error!"
774
841
 
775
842
  if _provider == "google":
776
843
  title = google_generated_title()
@@ -792,18 +859,18 @@ class SyntaxMUI:
792
859
 
793
860
  def process_query_stream(self, query: str, context: str, history: list, stream=True) -> Generator[str, None, None]:
794
861
 
795
- if not self._chat_profile:
862
+ if not self.chat_profile:
796
863
  chat_profile = _prof.get_profile("chat") or _prof.get_profile("admin")
797
864
  if not chat_profile:
798
865
  yield """<p style='color:red;'>Error: Chat profile is not configured. Add a chat profile inside the admin panel or contact your administrator.</p>
799
866
  """
800
867
  return None
801
- self._chat_profile = chat_profile
802
- self._chat_profile['client'] = _prof.get_client(chat_profile)
868
+ self.chat_profile = chat_profile
869
+ self.chat_profile['client'] = _prof.get_client(chat_profile)
803
870
 
804
- _provider = self._chat_profile['provider']
805
- _client = self._chat_profile['client']
806
- _model = self._chat_profile['model']
871
+ _provider = self.chat_profile['provider']
872
+ _client = self.chat_profile['client']
873
+ _model = self.chat_profile['model']
807
874
 
808
875
  _contents = f"""
809
876
  {self.smxai_instructions}\n\n
@@ -877,18 +944,18 @@ class SyntaxMUI:
877
944
 
878
945
  def process_query(self, query, context, history, stream=False):
879
946
 
880
- if not self._chat_profile:
947
+ if not self.chat_profile:
881
948
  chat_profile = _prof.get_profile("chat") or _prof.get_profile("admin")
882
949
  if not chat_profile:
883
950
  return """<p style='color:red;'>Error: Chat profile is not configured. Add a chat profile inside the admin panel or contact your administrator.</p>
884
951
  """
885
952
  return
886
953
 
887
- self._chat_profile = chat_profile
888
- self._chat_profile['client'] = _prof.get_client(chat_profile)
889
- _provider = self._chat_profile['provider']
890
- _client = self._chat_profile['client']
891
- _model = self._chat_profile['model']
954
+ self.chat_profile = chat_profile
955
+ self.chat_profile['client'] = _prof.get_client(chat_profile)
956
+ _provider = self.chat_profile['provider']
957
+ _client = self.chat_profile['client']
958
+ _model = self.chat_profile['model']
892
959
  _contents = f"""
893
960
  {self.smxai_instructions}\n\n
894
961
  Question: {query}\n
@@ -1027,7 +1094,7 @@ class SyntaxMUI:
1027
1094
  return code
1028
1095
  _prompt = f"```python\n{code}\n```"
1029
1096
 
1030
- repair_profile = _prof.get_profile("vision2text") or _prof.get_profile("admin")
1097
+ repair_profile = self.coder_profile
1031
1098
  if not repair_profile:
1032
1099
  return (
1033
1100
  '<div class="smx-alert smx-alert-warn">'
@@ -1137,21 +1204,28 @@ class SyntaxMUI:
1137
1204
  tasks = [str(t).strip().lower() for t in tasks if str(t).strip()]
1138
1205
 
1139
1206
  ai_profile = """
1140
- - You are a Python expert specializing in data science and machine learning.
1141
- - Your task is to generate a single, complete, production-quality, executable Python script for a Jupyter-like Python kernel, based on the given instructions.
1207
+ - You are a Python expert specialising in Data Science (DS) and Machine Learning (ML).
1208
+ - Your task is to generate a single, complete, production-ready Python script that can be executed in a Jupyter-like Python kernel, based on the given instructions.
1142
1209
  - The dataset is already loaded as a pandas DataFrame named `df` (no file I/O or file uploads).
1143
- - Make a copy of `df` and name it `df_copy`. Make sure `df_copy` is preprocessed and cleaned, named `df_cleaned`, if not already done so. Then use `df_cleaned` to perform the ML tasks described in the given context.
1144
- - Select your features and target, from `df_cleaned`, with care and name it `required_cols`
1145
- - Create your 'df_filtered by doing: df_filtered = df_cleaned[required_cols].
1146
- - Use the {TEMPLATE_CATALOGUE} below to educate yourself on which visualizations you will implement in the code.
1147
- - The final output MUST be the complete, executable Python code only, enclosed in a single markdown code block (```python ... ```), which is required to fulfill the user's request. See the {tasks} below.
1210
+ - Make a copy of `df` and name it `df_copy`.
1211
+ - Make sure `df_copy` is preprocessed and cleaned, and name it `df_cleaned`, if not already done so.
1212
+ - Work only with `df_cleaned` to perform the ML tasks described in the given context.
1213
+
1214
+ - Always treat modelling as features `X` and target `y`:
1215
+ * Choose ONE target column in `df_cleaned` (the value to be predicted) and refer to it as `target_col` or `y`.
1216
+ * Build the feature matrix `X` from `df_cleaned` WITHOUT including the target column or any direct transformation of it.
1217
+ * Examples of forbidden feature leakage: if predicting `sellingprice`, do NOT include `sellingprice`, `log_sellingprice`, `margin = sellingprice - mmr`, or any other direct function of `sellingprice` in `X`.
1218
+ * You may create target-derived columns (margins, flags, percentage differences) for descriptive tables or plots, but NEVER use them as model inputs.
1219
+
1220
+ - When you need a modelling frame, define `required_cols = [target_col] + feature_cols` where `feature_cols` excludes the target and its transforms, and then create `df_filtered = df_cleaned[required_cols]`.
1221
+
1222
+ - Use the {TEMPLATE_CATALOGUE} below to educate yourself about available helper functions and reference code, and ensure the implementations are in the code you generate.
1223
+ - The final output MUST BE the complete, executable Python code for the requested analysis, wrapped in a single fenced Python code block (```python ... ```), and MUST BE able to fulfil the user's request: {tasks}.
1148
1224
  - Do not include any explanatory text or markdown outside the code block.
1149
1225
  """
1150
1226
 
1151
1227
  TEMPLATE_CATALOGUE = """
1152
- ### Available SyntaxMatrix templates (use these instead of inventing new helpers)
1153
-
1154
- Visualisation templates (dataset-agnostic):
1228
+ Visualisation templates:
1155
1229
  - viz_pie(df, category_col=None, top_k=8): pie/donut shares within a category.
1156
1230
  - viz_stacked_bar(df, x=None, hue=None, normalise=True): composition across groups.
1157
1231
  - viz_count_bar(df, category_col=None, top_k=12): counts/denominators by category.
@@ -1191,9 +1265,9 @@ class SyntaxMUI:
1191
1265
 
1192
1266
  """
1193
1267
  ### Template rules
1194
- - You MAY call a template if it matches the task.
1268
+ - You MAY call 1 or more templates if they matche the task.
1195
1269
  - Do NOT invent template names.
1196
- - If no template fits, write minimal direct pandas/sklearn/seaborn code instead.
1270
+ - If no template fits, write minimal direct pandas/sklearn/seaborn code instead, for visualization.
1197
1271
  - Keep the solution short: avoid writing wrappers/utilities already handled by SyntaxMatrix hardener.
1198
1272
 
1199
1273
  #### Template selection hint examples:
@@ -1216,8 +1290,7 @@ class SyntaxMUI:
1216
1290
  set `random_state=42` where relevant.
1217
1291
  4) Be defensive, but avoid hard-failing on optional fields:
1218
1292
  - If the primary column, needed to answer the question, is missing, review your copy of the `df` again.
1219
- Make sure that you selected the proper column.
1220
- Never use a column/variable which isn't available or defined.
1293
+ - Make sure that you selected the proper column. Never use a column/variable which isn't available or defined.
1221
1294
  - If a secondary/extra column is missing, show a warning with `show(...)` and continue using available fields.
1222
1295
  - Handle missing values sensibly (drop rows for simple EDA; use `ColumnTransformer` + `SimpleImputer` for modelling).
1223
1296
  - For categorical features in ML, use `OneHotEncoder(handle_unknown="ignore")`
@@ -1249,6 +1322,20 @@ class SyntaxMUI:
1249
1322
  11) You MUST NOT reference any column outside Available columns: {AVAILABLE_COLUMNS}.
1250
1323
  12) If asked to predict/classify, choose the target by matching the task text to Allowed columns
1251
1324
  and never invent a new name.
1325
+ 13) Treat df as the primary dataset you must work with.
1326
+ 14) The dataset is already loaded as df (no file I/O or file uploads).
1327
+ 15) All outputs must be visible to the user via the provided show(...) helper.
1328
+ 16) Never use print(...); use show(...) instead.
1329
+ 17) You MUST NOT read from or write to local files, folders, or external storage.
1330
+ - Do not call open(...), Path(...).write_text/write_bytes, or similar file APIs.
1331
+ - Do not use df.to_csv(...), df.to_excel(...), df.to_parquet(...),
1332
+ df.to_pickle(...), df.to_json(...), df.to_hdf(...), or any other
1333
+ method that writes to disk.
1334
+ - Do not call joblib.dump(...), pickle.dump(...), torch.save(...),
1335
+ numpy.save(...), numpy.savetxt(...), or similar saving functions.
1336
+ - Do not call plt.savefig(..., 'somefile.png') or any variant that
1337
+ writes an image to a filename. Plots must be rendered in-memory only.
1338
+ 18) Keep everything in memory and surface results via show(...) or plots.
1252
1339
 
1253
1340
  #### Cohort rules
1254
1341
  When you generate plots for cohorts or categories, you MUST obey these rules:
@@ -1297,21 +1384,21 @@ class SyntaxMUI:
1297
1384
  - And ends with at least 3 visible output (`show(...)` and/or `plt.show()`).
1298
1385
  """)
1299
1386
 
1300
- if not self._coding_profile:
1301
- coding_profile = _prof.get_profile("coding") or _prof.get_profile("admin")
1302
- if not coding_profile:
1387
+ if not self.coder_profile:
1388
+ _coder_profile = _prof.get_profile("coder")
1389
+ if not _coder_profile:
1303
1390
  return (
1304
1391
  '<div class="smx-alert smx-alert-warn">'
1305
- 'No LLM profile configured for <code>coding</code> (or <code>admin</code>). <br>'
1392
+ 'No LLM profile configured for <code>coding</code> <br>'
1306
1393
  'Please, add the LLM profile inside the admin panel or contact your Administrator.'
1307
1394
  '</div>'
1308
1395
  )
1309
1396
 
1310
- self._coding_profile = coding_profile
1311
- self._coding_profile['client'] = _prof.get_client(coding_profile)
1397
+ self.coder_profile = _coder_profile
1398
+ self.coder_profile['client'] = _prof.get_client(_coder_profile)
1312
1399
 
1313
1400
  # code = mlearning_agent(instructions, ai_profile, self._coding_profile)
1314
- code, usage = mlearning_agent(instructions, ai_profile, self._coding_profile)
1401
+ code, usage = mlearning_agent(instructions, ai_profile, self.coder_profile)
1315
1402
  self._last_llm_usage = usage
1316
1403
 
1317
1404
  if code:
@@ -1359,7 +1446,22 @@ class SyntaxMUI:
1359
1446
  return code.strip()
1360
1447
 
1361
1448
  return "Error: AI code generation failed."
1449
+
1362
1450
 
1451
+ def get_image_generator_profile(self):
1452
+ if not self._fullvision_profile:
1453
+ fullvision_profile = _prof.get_profile("fullvision")
1454
+ if not fullvision_profile:
1455
+ return (
1456
+ '<div class="smx-alert smx-alert-warn">'
1457
+ 'No Full Vision profile configured for <code>coding</code> <br>'
1458
+ 'Please, add it inside the admin panel or contact your Administrator.'
1459
+ '</div>'
1460
+ )
1461
+ self._fullvision_profile = fullvision_profile
1462
+ self._fullvision_profile['client'] = _prof.get_client(fullvision_profile)
1463
+
1464
+ return self._fullvision_profile
1363
1465
 
1364
1466
  def sanitize_rough_to_markdown_task(self, rough: str) -> str:
1365
1467
  """
@@ -1395,7 +1497,13 @@ class SyntaxMUI:
1395
1497
 
1396
1498
  # Drop optional <rough> wrapper
1397
1499
  return out.replace("<rough>", "").replace("</rough>", "").strip()
1398
-
1500
+
1501
+ def current_profile(self, agency):
1502
+ current_profile = _prof.get_profile(agency) or _prof.get_profile('admin')
1503
+ if not current_profile:
1504
+ return "Error: Configure the valid LLM profile."
1505
+ current_profile['client'] = _prof.get_client(current_profile)
1506
+ return current_profile
1399
1507
 
1400
1508
  def run(self):
1401
1509
  url = f"http://{self.host}:{self.port}/"