syntaxmatrix 2.2.8__tar.gz → 2.3.0__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.
Files changed (69) hide show
  1. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/PKG-INFO +2 -1
  2. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/SyntaxMatrix.egg-info/PKG-INFO +2 -1
  3. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/SyntaxMatrix.egg-info/requires.txt +1 -0
  4. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/setup.py +2 -1
  5. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/__init__.py +2 -2
  6. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/commentary.py +45 -33
  7. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/core.py +101 -10
  8. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/kernel_manager.py +1 -1
  9. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/routes.py +27 -41
  10. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/prompts.py +3 -3
  11. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/LICENSE.txt +0 -0
  12. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/README.md +0 -0
  13. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/SyntaxMatrix.egg-info/SOURCES.txt +0 -0
  14. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/SyntaxMatrix.egg-info/dependency_links.txt +0 -0
  15. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/SyntaxMatrix.egg-info/top_level.txt +0 -0
  16. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/pyproject.toml +0 -0
  17. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/setup.cfg +0 -0
  18. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/auth.py +0 -0
  19. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/bootstrap.py +0 -0
  20. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/db.py +0 -0
  21. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/display.py +0 -0
  22. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/emailer.py +0 -0
  23. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/file_processor.py +0 -0
  24. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/generate_page.py +0 -0
  25. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/gpt_models_latest.py +0 -0
  26. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/history_store.py +0 -0
  27. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/llm_store.py +0 -0
  28. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/model_templates.py +0 -0
  29. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/models.py +0 -0
  30. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/plottings.py +0 -0
  31. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/profiles.py +0 -0
  32. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/project_root.py +0 -0
  33. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/session.py +0 -0
  34. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/__init__.py +0 -0
  35. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/default.yaml +0 -0
  36. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/logging.py +0 -0
  37. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/model_map.py +0 -0
  38. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/settings/string_navbar.py +0 -0
  39. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/smiv.py +0 -0
  40. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/smpv.py +0 -0
  41. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/css/style.css +0 -0
  42. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/docs.md +0 -0
  43. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/icons/favicon.png +0 -0
  44. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/icons/hero_bg.jpg +0 -0
  45. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/icons/logo.png +0 -0
  46. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/icons/svg_497526.svg +0 -0
  47. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/icons/svg_497528.svg +0 -0
  48. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/js/chat.js +0 -0
  49. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/js/sidebar.js +0 -0
  50. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/static/js/widgets.js +0 -0
  51. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/code_cell.html +0 -0
  52. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/dashboard.html +0 -0
  53. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/docs.html +0 -0
  54. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/error.html +0 -0
  55. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/login.html +0 -0
  56. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/templates/register.html +0 -0
  57. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/themes.py +0 -0
  58. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/ui_modes.py +0 -0
  59. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/utils.py +0 -0
  60. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vector_db.py +0 -0
  61. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/__init__.py +0 -0
  62. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/adapters/__init__.py +0 -0
  63. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/adapters/milvus_adapter.py +0 -0
  64. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/adapters/pgvector_adapter.py +0 -0
  65. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/adapters/sqlite_adapter.py +0 -0
  66. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/base.py +0 -0
  67. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectordb/registry.py +0 -0
  68. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/vectorizer.py +0 -0
  69. {syntaxmatrix-2.2.8 → syntaxmatrix-2.3.0}/syntaxmatrix/workspace_db.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syntaxmatrix
3
- Version: 2.2.8
3
+ Version: 2.3.0
4
4
  Summary: SyntaxMUI: A customizable framework for Python AI Assistant Projects.
5
5
  Home-page: https://github.com/bobganti/syntaxmatrix_demo
6
6
  Author: Bob Nti
@@ -24,6 +24,7 @@ Requires-Dist: openai>=1.84.0
24
24
  Requires-Dist: google-genai>=1.19.0
25
25
  Requires-Dist: anthropic>=0.67.0
26
26
  Requires-Dist: reportlab>=4.4.3
27
+ Requires-Dist: lxml>=6.0.2
27
28
  Provides-Extra: mlearning
28
29
  Requires-Dist: pandas>=2.2.3; extra == "mlearning"
29
30
  Requires-Dist: numpy>=2.0.2; extra == "mlearning"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syntaxmatrix
3
- Version: 2.2.8
3
+ Version: 2.3.0
4
4
  Summary: SyntaxMUI: A customizable framework for Python AI Assistant Projects.
5
5
  Home-page: https://github.com/bobganti/syntaxmatrix_demo
6
6
  Author: Bob Nti
@@ -24,6 +24,7 @@ Requires-Dist: openai>=1.84.0
24
24
  Requires-Dist: google-genai>=1.19.0
25
25
  Requires-Dist: anthropic>=0.67.0
26
26
  Requires-Dist: reportlab>=4.4.3
27
+ Requires-Dist: lxml>=6.0.2
27
28
  Provides-Extra: mlearning
28
29
  Requires-Dist: pandas>=2.2.3; extra == "mlearning"
29
30
  Requires-Dist: numpy>=2.0.2; extra == "mlearning"
@@ -10,6 +10,7 @@ openai>=1.84.0
10
10
  google-genai>=1.19.0
11
11
  anthropic>=0.67.0
12
12
  reportlab>=4.4.3
13
+ lxml>=6.0.2
13
14
 
14
15
  [:sys_platform == "win32"]
15
16
  pywin32>=311
@@ -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.2.8",
11
+ version="2.3.0",
12
12
  author="Bob Nti",
13
13
  author_email="bob.nti@syntaxmatrix.com",
14
14
  description="SyntaxMUI: A customizable framework for Python AI Assistant Projects.",
@@ -38,6 +38,7 @@ setup(
38
38
  "google-genai>=1.19.0",
39
39
  "anthropic>=0.67.0",
40
40
  "reportlab>=4.4.3",
41
+ "lxml>=6.0.2"
41
42
  ],
42
43
  extras_require={
43
44
  "mlearning": [
@@ -59,6 +59,6 @@ stream_write = _app_instance.stream_write
59
59
  enable_stream = _app_instance.enable_stream
60
60
  stream = _app_instance.stream
61
61
  get_stream_args = _app_instance.get_stream_args
62
- # set_stream_args = _app_instance.set_stream_args
63
62
 
64
- app = _app_instance.app
63
+
64
+ app = _app_instance.app
@@ -153,35 +153,33 @@ def _context_strings(context: Dict[str, Any]) -> List[str]:
153
153
  return out
154
154
 
155
155
 
156
- # ---LLM prompts ---
157
-
158
- _SYSTEM_VISION = (
156
+ def phrase_commentary_vision(context: Dict[str, Any], images_b64: List[str]) -> str:
157
+ """
158
+ Use the project's 'vision2text' profile (profiles.py). If the provider supports images,
159
+ send figures + text; otherwise fall back to a text-only prompt grounded by labels.
160
+ """
161
+
162
+ _SYSTEM_VISION = (
159
163
  "You are a data analyst. Write a short, precise commentary in UK English that explains what the "
160
164
  "already-rendered visuals mean for the user's question. "
161
165
  "Use information visible in the attached figures and the provided context strings (field names, labels). "
162
166
  "Do not invent numbers. If the figures/context are insufficient, say: 'Insufficient context to comment usefully.'"
163
- )
167
+ )
164
168
 
165
- _USER_TMPL_VISION = """\
166
- Question:
167
- {q}
169
+ _USER_TMPL_VISION = """\
170
+ question:
171
+ {q}
168
172
 
169
- Visible context strings (titles, axes, legends, headers):
170
- {ctx}
173
+ Visible context strings (titles, axes, legends, headers):
174
+ {ctx}
171
175
 
172
- Write a concise conclusion (~200-260 words) with:
173
- - <strong>Headline</strong> (one sentence answering the question).
174
- - <strong>Evidence</strong> (6-8 bullets referencing panels/axes/legend groups seen in the figures and explaining the plots/tables vis-a-vis the query). Explain all the oupupt comprehensively in details.
175
- - <strong>Limitations</strong> (1 bullets; avoid quoting numbers unless present in context).
176
- - <strong>Next step</strong> (1 bullet).
177
- """
178
-
179
- def phrase_commentary_vision(context: Dict[str, Any], images_b64: List[str]) -> str:
176
+ Write a concise conclusion (~200-260 words) with:
177
+ - <strong>Headline</strong> (one sentence answering the question).
178
+ - <strong>Evidence</strong> (6-8 bullets referencing panels/axes/legend groups seen in the figures and explaining the plots/tables vis-a-vis the query). Explain all the oupupt comprehensively in details.
179
+ - <strong>Limitations</strong> (1 bullets; avoid quoting numbers unless present in context).
180
+ - <strong>Next step</strong> (1 bullet).
180
181
  """
181
- Use the project's 'vision2text' profile (profiles.py). If the provider supports images,
182
- send figures + text; otherwise fall back to a text-only prompt grounded by labels.
183
- """
184
-
182
+
185
183
  visible = _context_strings(context)
186
184
  user = _USER_TMPL_VISION.format(
187
185
  q=context.get("question",""),
@@ -202,22 +200,36 @@ def phrase_commentary_vision(context: Dict[str, Any], images_b64: List[str]) ->
202
200
  # Google
203
201
  if _provider == "google":
204
202
  try:
205
- parts = [types.Part.from_text(text=user)]
203
+ # Gemini expects a different structure
204
+ contents = []
205
+
206
+ # Add text part first
207
+ text_part = {
208
+ "text": _SYSTEM_VISION + "\n\n" + user
209
+ }
210
+ contents.append(text_part)
211
+
212
+ # Add image parts
206
213
  for b64 in images_b64[:4]:
207
214
  if b64:
208
- parts.append(types.Part.from_bytes(data=base64.b64decode(b64), mime_type="image/png"))
209
- contents = [types.Content(role="user", parts=parts)]
210
- resp = _client.models.generate_content(
215
+ image_part = {
216
+ "inline_data": {
217
+ "mime_type": "image/png",
218
+ "data": b64
219
+ }
220
+ }
221
+ contents.append(image_part)
222
+
223
+ # Correct Gemini API call
224
+ response = _client.models.generate_content(
211
225
  model=_model,
212
- contents=contents,
213
- system_instruction=_SYSTEM_VISION
226
+ contents=contents
214
227
  )
215
- txt = getattr(resp, "text", "") or ""
216
- if txt.strip():
217
- return txt.strip()
218
- except Exception:
219
- pass # Fall through to the chat.completions fallback implemented below.
220
-
228
+ txt = response.text.strip()
229
+ return txt.strip()
230
+ except Exception as e:
231
+ return f"Google Gemini error: {e}"
232
+
221
233
  # Openai
222
234
  elif _provider == "openai" and _model in GPT_MODELS_LATEST:
223
235
  # Use the Responses API with multimodal input (text + up to 4 images)
@@ -14,7 +14,7 @@ from .file_processor import process_admin_pdf_files
14
14
  from google.genai import types
15
15
  from .vector_db import query_embeddings
16
16
  from .vectorizer import embed_text
17
- from syntaxmatrix.settings.prompts import SMXAI_CHAT_ID, SMXAI_CHAT_INSTRUCTIONS, SMX_WEBSITE_DESCRIPTION
17
+ from syntaxmatrix.settings.prompts import SMXAI_CHAT_ID, SMXAI_CHAT_INSTRUCTIONS, SMXAI_WEBSITE_DESCRIPTION
18
18
  from typing import List, Generator
19
19
  from .auth import init_auth_db
20
20
  from . import profiles as _prof
@@ -26,6 +26,7 @@ from dotenv import load_dotenv
26
26
  from html import unescape
27
27
 
28
28
 
29
+
29
30
  # ──────── framework‐local storage paths ────────
30
31
  # this ensures the key & data always live under the package dir,
31
32
  # regardless of where the developer `cd` into before launching.
@@ -71,7 +72,7 @@ class SyntaxMUI:
71
72
  self.user_files_enabled = False
72
73
  self.ai_chat_id = SMXAI_CHAT_ID
73
74
  self.ai_chat_instructions = SMXAI_CHAT_INSTRUCTIONS
74
- self.website_description = SMX_WEBSITE_DESCRIPTION
75
+ self.website_description = SMXAI_WEBSITE_DESCRIPTION
75
76
 
76
77
  db.init_db()
77
78
  self.page = ""
@@ -611,9 +612,12 @@ class SyntaxMUI:
611
612
  if _provider == "google":
612
613
  intent = google_classify_query()
613
614
  return intent
614
- elif _model in self.gpt_models_latest():
615
+ if _model in self.gpt_models_latest():
615
616
  intent = gpt_models_latest_classify_query()
616
617
  return intent
618
+ if _provider == "anthropic":
619
+ intent = anthropic_classify_query()
620
+ return intent
617
621
  else:
618
622
  intent = openai_sdk_classify_query()
619
623
  return intent
@@ -811,7 +815,6 @@ class SyntaxMUI:
811
815
  yield token
812
816
  except Exception as e:
813
817
  yield f"Error during streaming: {type(e).__name__}: {e}"
814
-
815
818
 
816
819
  def process_query(self, query, context, history, stream=False):
817
820
 
@@ -1005,13 +1008,83 @@ class SyntaxMUI:
1005
1008
  - Solves: {question},
1006
1009
  - And ends with at least one visible output (`show(...)` and/or `plt.show()`).
1007
1010
  """
1008
-
1011
+
1009
1012
  def google_generate_code():
1010
- response = _client.models.generate_content(
1011
- model=_model,
1012
- contents=f"{ai_profile}\n\n{instructions}"
1013
- )
1014
- return response.text
1013
+ try:
1014
+ # Combine system prompt and instructions for Gemini
1015
+ full_prompt = f"{ai_profile}\n\n{instructions}"
1016
+
1017
+ # Gemini expects a simple generate_content call with the model and contents
1018
+ response = _client.models.generate_content(
1019
+ model=_model,
1020
+ contents=full_prompt
1021
+ )
1022
+
1023
+ # Extract text from response
1024
+ if hasattr(response, 'text'):
1025
+ return response.text
1026
+ elif hasattr(response, 'candidates') and response.candidates:
1027
+ candidate = response.candidates[0]
1028
+ if hasattr(candidate.content, 'parts'):
1029
+ return ''.join(part.text for part in candidate.content.parts if hasattr(part, 'text'))
1030
+ return str(response)
1031
+
1032
+ except Exception as e:
1033
+ print(f"Google Gemini code generation error: {e}")
1034
+ # Return a basic analysis code with ALL necessary imports
1035
+ return """
1036
+ import pandas as pd
1037
+ import matplotlib.pyplot as plt
1038
+ import seaborn as sns
1039
+ import numpy as np
1040
+ import io
1041
+ import base64
1042
+ from syntaxmatrix.display import show
1043
+
1044
+ print("Basic DataFrame Info:")
1045
+ print(f"Shape: {df.shape}")
1046
+ print("\\nColumns and dtypes:")
1047
+ print(df.dtypes)
1048
+ print("\\nBasic statistics:")
1049
+ show(df.describe())
1050
+
1051
+ print("\\nFirst few rows:")
1052
+ show(df.head())
1053
+
1054
+ # Generate a simple visualization based on available columns
1055
+ plt.figure(figsize=(10, 6))
1056
+
1057
+ if len(df.columns) >= 2:
1058
+ # Try to find numeric columns for scatter plot
1059
+ numeric_cols = df.select_dtypes(include=['number']).columns
1060
+ if len(numeric_cols) >= 2:
1061
+ sns.scatterplot(data=df, x=numeric_cols[0], y=numeric_cols[1])
1062
+ plt.title(f"Scatter plot: {numeric_cols[0]} vs {numeric_cols[1]}")
1063
+ plt.tight_layout()
1064
+ plt.show()
1065
+ else:
1066
+ # Use first column for bar plot
1067
+ top_values = df[df.columns[0]].value_counts().head(10)
1068
+ top_values.plot(kind='bar')
1069
+ plt.title(f"Top 10 values in {df.columns[0]}")
1070
+ plt.tight_layout()
1071
+ plt.show()
1072
+ else:
1073
+ # Single column analysis
1074
+ if len(df.columns) == 1:
1075
+ col_name = df.columns[0]
1076
+ if df[col_name].dtype in ['object', 'category']:
1077
+ df[col_name].value_counts().head(10).plot(kind='bar')
1078
+ plt.title(f"Value counts for {col_name}")
1079
+ else:
1080
+ df[col_name].hist(bins=20)
1081
+ plt.title(f"Distribution of {col_name}")
1082
+ plt.tight_layout()
1083
+ plt.show()
1084
+ else:
1085
+ print("Insufficient columns for detailed analysis")
1086
+ show(df)
1087
+ """
1015
1088
 
1016
1089
  def gpt_models_latest_generate_code(reasoning_effort = "medium", verbosity = "medium"):
1017
1090
  try:
@@ -1077,6 +1150,24 @@ class SyntaxMUI:
1077
1150
 
1078
1151
  code = strip_describe_slice(code)
1079
1152
  code = drop_bad_classification_metrics(code, df)
1153
+
1154
+ if "import io" not in code and "io.BytesIO" in code:
1155
+ lines = code.split('\n')
1156
+ import_lines = []
1157
+ other_lines = []
1158
+
1159
+ for line in lines:
1160
+ if line.strip().startswith('import ') or line.strip().startswith('from '):
1161
+ import_lines.append(line)
1162
+ else:
1163
+ other_lines.append(line)
1164
+
1165
+ # Add missing io import
1166
+ if "import io" not in '\n'.join(import_lines):
1167
+ import_lines.append('import io')
1168
+
1169
+ code = '\n'.join(import_lines + [''] + other_lines)
1170
+
1080
1171
  return code.strip()
1081
1172
 
1082
1173
 
@@ -48,7 +48,7 @@ class SyntaxMatrixKernelManager:
48
48
 
49
49
  _df_cache = None
50
50
 
51
- def execute_code_in_kernel(kc, code, timeout=90):
51
+ def execute_code_in_kernel(kc, code, timeout=120):
52
52
 
53
53
  _local_stdout = ""
54
54
  _local_stderr = ""
@@ -50,9 +50,9 @@ _stream_q = queue.Queue()
50
50
  _stream_cancelled = {}
51
51
  _last_result_html = {} # { session_id: html_doc }
52
52
 
53
- # store per-client so it stays alongside uploads
54
- MLEARNING_OUTPUTS = os.path.join(_CLIENT_DIR, "mlearning_outputs")
55
- os.makedirs(MLEARNING_OUTPUTS, exist_ok=True)
53
+ # # store per-client so it stays alongside uploads
54
+ # MLEARNING_OUTPUTS = os.path.join(_CLIENT_DIR, "mlearning_outputs")
55
+ # os.makedirs(MLEARNING_OUTPUTS, exist_ok=True)
56
56
 
57
57
  # single, reused formatter: inline styles, padding, rounded corners, scroll
58
58
  _FMT = _HtmlFmt(
@@ -5230,7 +5230,7 @@ def setup_routes(smx):
5230
5230
  )
5231
5231
  ).strip()
5232
5232
 
5233
- elif prof["provider"] == "openai":
5233
+ elif prof["provider"] == "openai" and _model in smx.gpt_models_latest():
5234
5234
  args = set_args(model=prof.get("model"), instructions=_CELL_REPAIR_RULES,
5235
5235
  input=_prompt, previous_id=None, store=False,
5236
5236
  reasoning_effort="minimal", verbosity="low")
@@ -5388,30 +5388,13 @@ def setup_routes(smx):
5388
5388
  # 2.4 Append a single download button (explicit click → fetch → download)
5389
5389
  download_url = url_for("download_result_html", session_id=session_id)
5390
5390
  dl_html = f"""
5391
- <button type="button"
5392
- class="btn"
5393
- style="margin:14px 0;padding:8px 12px;border:1px solid #0b6;border-radius:6px;background:#fff;color:#0b6;cursor:pointer;"
5394
- onclick="(async function(btn) {{
5395
- try {{
5396
- btn.disabled = true;
5397
- const orig = btn.textContent;
5398
- btn.textContent = 'Saving…';
5399
- const resp = await fetch('{download_url}', {{
5400
- method: 'POST',
5401
- headers: {{ 'X-Requested-With': 'fetch' }}
5402
- }});
5403
- const data = await resp.json();
5404
- if (!resp.ok || !data.ok) throw new Error(data.error || 'Save failed');
5405
- btn.textContent = 'Saved to MLEARNING_OUTPUTS';
5406
- }} catch (err) {{
5407
- console.error(err);
5408
- alert('Could not save. Please try again.');
5409
- }} finally {{
5410
- btn.disabled = false;
5411
- }}
5412
- }})(this)">
5413
- Download
5414
- </button>
5391
+ <a href="{download_url}">
5392
+ <button type="button"
5393
+ class="btn"
5394
+ style="margin:14px 0;padding:8px 12px;border:1px solid #0b6;border-radius:6px;background:#fff;color:#0b6;cursor:pointer;">
5395
+ Download
5396
+ </button>
5397
+ </a>
5415
5398
  """
5416
5399
  ai_outputs.append(Markup(dl_html))
5417
5400
 
@@ -5489,26 +5472,29 @@ def setup_routes(smx):
5489
5472
  data_cells=data_cells,
5490
5473
  session_id=session_id,
5491
5474
  )
5475
+
5492
5476
 
5493
-
5494
- @smx.app.route("/download/result/html/<session_id>", methods=["POST"])
5477
+ @smx.app.route("/download/result/html/<session_id>", methods=["GET"])
5495
5478
  def download_result_html(session_id):
5496
- """Send the last-built result HTML as a download (only when user clicks)."""
5497
- os.makedirs(MLEARNING_OUTPUTS, exist_ok=True)
5498
- stamp = datetime.now().strftime("%Y%m%d-%H%M%S-%f")
5499
- filename = f"result_{session_id}_{stamp}.html"
5500
- path = os.path.join(MLEARNING_OUTPUTS, filename)
5501
-
5479
+ """Stream the last-built result HTML as a browser download (no server save)."""
5502
5480
  html_doc = _last_result_html.get(session_id)
5503
5481
  if not html_doc:
5504
- return jsonify({"ok": False, "error": "No result available."}), 404
5505
- with open(path, "w", encoding="utf-8") as f:
5506
- f.write(html_doc)
5482
+ return ("No result available.", 404)
5507
5483
 
5508
- # free memory for this session copy (optional)
5484
+ buf = io.BytesIO(html_doc.encode("utf-8"))
5485
+ buf.seek(0)
5486
+
5487
+ # keep a copy if you wish, or free it:
5509
5488
  _last_result_html.pop(session_id, None)
5510
5489
 
5511
- return jsonify({"ok": True, "path": path})
5490
+ stamp = datetime.now().strftime("%Y%m%d-%H%M%S-%f")
5491
+ filename = f"result_{session_id}_{stamp}.html"
5492
+ return send_file(
5493
+ buf,
5494
+ mimetype="text/html; charset=utf-8",
5495
+ as_attachment=True,
5496
+ download_name=filename
5497
+ )
5512
5498
 
5513
5499
  # ── UPLOAD DATASET --------------------------------------
5514
5500
  @smx.app.route("/dashboard/upload", methods=["POST"])
@@ -40,7 +40,7 @@ SMXAI_CHAT_INSTRUCTIONS = """
40
40
  8. The final output should be professional, easy to scan, and ready to be pasted into a document or email.
41
41
  """
42
42
 
43
- SMX_WEBSITE_DESCRIPTION = F"""
43
+ SMXAI_WEBSITE_DESCRIPTION = F"""
44
44
  SyntaxMatrix Overview
45
45
  SyntaxMatrix is a battle-tested Python framework that accelerates AI application development from concept to production, slashing engineering overhead by up to 80%. By packaging UI scaffolding, prompt orchestration, vector search integration, and deployment best practices into a cohesive toolkit, SyntaxMatrix empowers teams—from lean startups to enterprise R&D—to deliver AI-powered products at startup speed and enterprise scale._
46
46
  ____________________________________
@@ -97,7 +97,7 @@ SMX_WEBSITE_DESCRIPTION = F"""
97
97
  """
98
98
 
99
99
  SMX_PAGE_GENERATION_INSTRUCTIONS = f"""
100
- 0· Parse the Website Description (MANDATORY):\n{SMX_WEBSITE_DESCRIPTION}\n\n
100
+ 0· Parse the Website Description (MANDATORY):\n{SMXAI_WEBSITE_DESCRIPTION}\n\n
101
101
  1. Input always contains:
102
102
  • website_description - plain-text overview of the site/company (mission, goals, audience, visual style, etc.).
103
103
  • page_title - the specific page to create (e.g. About, Pricing, Blog).
@@ -163,7 +163,7 @@ SMX_PAGE_GENERATION_INSTRUCTIONS = f"""
163
163
  """
164
164
 
165
165
  WEBPAGE_GENERATION_INSTRUCTIONS = (f"""
166
- Parse the Website Description (MANDATORY):\n{SMX_WEBSITE_DESCRIPTION}\n\n
166
+ Parse the Website Description (MANDATORY):\n{SMXAI_WEBSITE_DESCRIPTION}\n\n
167
167
  Prompt (give this to the LLM)
168
168
  Role: You are a senior front-end engineer and content designer building a single HTML page for the brand SyntaxMatrix. Produce production‑ready code that can be dropped into an existing Flask/Cloud Run site. Do not include a navbar or a footer; the host site renders them.
169
169
 
File without changes
File without changes
File without changes