syntaxmatrix 2.5.6__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.
- syntaxmatrix/agentic/agents.py +1220 -169
- syntaxmatrix/agentic/agents_orchestrer.py +326 -0
- syntaxmatrix/agentic/code_tools_registry.py +27 -32
- syntaxmatrix/commentary.py +16 -16
- syntaxmatrix/core.py +185 -81
- syntaxmatrix/db.py +460 -4
- syntaxmatrix/{display.py → display_html.py} +2 -6
- syntaxmatrix/gpt_models_latest.py +1 -1
- syntaxmatrix/media/__init__.py +0 -0
- syntaxmatrix/media/media_pixabay.py +277 -0
- syntaxmatrix/models.py +1 -1
- syntaxmatrix/page_builder_defaults.py +183 -0
- syntaxmatrix/page_builder_generation.py +1122 -0
- syntaxmatrix/page_layout_contract.py +644 -0
- syntaxmatrix/page_patch_publish.py +1471 -0
- syntaxmatrix/preface.py +142 -21
- syntaxmatrix/profiles.py +28 -10
- syntaxmatrix/routes.py +1740 -453
- syntaxmatrix/selftest_page_templates.py +360 -0
- syntaxmatrix/settings/client_items.py +28 -0
- syntaxmatrix/settings/model_map.py +1022 -207
- syntaxmatrix/settings/prompts.py +328 -130
- syntaxmatrix/static/assets/hero-default.svg +22 -0
- syntaxmatrix/static/icons/bot-icon.png +0 -0
- syntaxmatrix/static/icons/favicon.png +0 -0
- syntaxmatrix/static/icons/logo.png +0 -0
- syntaxmatrix/static/icons/logo3.png +0 -0
- syntaxmatrix/templates/admin_branding.html +104 -0
- syntaxmatrix/templates/admin_features.html +63 -0
- syntaxmatrix/templates/admin_secretes.html +108 -0
- syntaxmatrix/templates/dashboard.html +296 -133
- syntaxmatrix/templates/dataset_resize.html +535 -0
- syntaxmatrix/templates/edit_page.html +2535 -0
- syntaxmatrix/utils.py +2431 -2383
- {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/METADATA +6 -2
- {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/RECORD +39 -24
- syntaxmatrix/generate_page.py +0 -644
- syntaxmatrix/static/icons/hero_bg.jpg +0 -0
- {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/WHEEL +0 -0
- {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/licenses/LICENSE.txt +0 -0
- {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/top_level.txt +0 -0
syntaxmatrix/preface.py
CHANGED
|
@@ -26,6 +26,7 @@ __all__ = [
|
|
|
26
26
|
"SB_boxplot",
|
|
27
27
|
"SB_scatterplot",
|
|
28
28
|
"SB_heatmap",
|
|
29
|
+
"viz_stacked_bar",
|
|
29
30
|
|
|
30
31
|
# core helpers (all underscore-prefixed functions)
|
|
31
32
|
"_SMX_caption_from_ctx",
|
|
@@ -68,12 +69,12 @@ except Exception:
|
|
|
68
69
|
class _Dummy:
|
|
69
70
|
def __getattr__(self, name):
|
|
70
71
|
def _f(*a, **k):
|
|
71
|
-
from syntaxmatrix.
|
|
72
|
+
from syntaxmatrix.display_html import show
|
|
72
73
|
show('⚠ seaborn not available; plot skipped.')
|
|
73
74
|
return _f
|
|
74
75
|
sns = _Dummy()
|
|
75
76
|
|
|
76
|
-
from syntaxmatrix.
|
|
77
|
+
from syntaxmatrix.display_html import show as _SMX_base_show
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
boxplot = barplot = histplot = distplot = lineplot = countplot = heatmap = pairplot = None
|
|
@@ -134,11 +135,32 @@ def _SMX_axes_have_titles(fig=None):
|
|
|
134
135
|
def _SMX_export_png():
|
|
135
136
|
import io, base64
|
|
136
137
|
fig = plt.gcf()
|
|
138
|
+
|
|
139
|
+
# If the figure has no real data, skip exporting to avoid blank images.
|
|
140
|
+
try:
|
|
141
|
+
axes = fig.get_axes()
|
|
142
|
+
has_data = any(
|
|
143
|
+
getattr(ax, "has_data", lambda: False)()
|
|
144
|
+
for ax in axes
|
|
145
|
+
)
|
|
146
|
+
except Exception:
|
|
147
|
+
has_data = True # fail open: better a plot than nothing if check breaks
|
|
148
|
+
|
|
149
|
+
if not has_data:
|
|
150
|
+
try:
|
|
151
|
+
from syntaxmatrix.display_html import show as _show
|
|
152
|
+
_show("⚠ Plot skipped: figure has no data to export.")
|
|
153
|
+
except Exception:
|
|
154
|
+
pass
|
|
155
|
+
plt.close(fig)
|
|
156
|
+
return
|
|
157
|
+
|
|
137
158
|
try:
|
|
138
159
|
if not _SMX_axes_have_titles(fig):
|
|
139
160
|
fig.suptitle(_SMX_caption_from_ctx(), fontsize=10)
|
|
140
161
|
except Exception:
|
|
141
162
|
pass
|
|
163
|
+
|
|
142
164
|
buf = io.BytesIO()
|
|
143
165
|
plt.savefig(buf, format='png', bbox_inches='tight')
|
|
144
166
|
buf.seek(0)
|
|
@@ -150,7 +172,7 @@ def _SMX_export_png():
|
|
|
150
172
|
"style='max-width:100%;height:auto;border:1px solid #ccc;border-radius:4px;'/>"
|
|
151
173
|
)
|
|
152
174
|
)
|
|
153
|
-
plt.close()
|
|
175
|
+
plt.close(fig)
|
|
154
176
|
|
|
155
177
|
|
|
156
178
|
def _pick_df():
|
|
@@ -228,7 +250,7 @@ def _safe_plot(func, *args, **kwargs):
|
|
|
228
250
|
ax = plt.gca()
|
|
229
251
|
try:
|
|
230
252
|
if hasattr(ax, 'has_data') and not ax.has_data():
|
|
231
|
-
from syntaxmatrix.
|
|
253
|
+
from syntaxmatrix.display_html import show as _show
|
|
232
254
|
_show('⚠ Empty plot: no data drawn.')
|
|
233
255
|
except Exception:
|
|
234
256
|
pass
|
|
@@ -238,7 +260,7 @@ def _safe_plot(func, *args, **kwargs):
|
|
|
238
260
|
pass
|
|
239
261
|
return ax
|
|
240
262
|
except Exception as e:
|
|
241
|
-
from syntaxmatrix.
|
|
263
|
+
from syntaxmatrix.display_html import show as _show
|
|
242
264
|
_show(f'⚠ Plot skipped: {type(e).__name__}: {e}')
|
|
243
265
|
return None
|
|
244
266
|
|
|
@@ -398,7 +420,7 @@ def SB_heatmap(*a, **k):
|
|
|
398
420
|
except Exception:
|
|
399
421
|
data = None
|
|
400
422
|
if data is None:
|
|
401
|
-
from syntaxmatrix.
|
|
423
|
+
from syntaxmatrix.display_html import show as _show
|
|
402
424
|
_show('⚠ Heatmap skipped: no data.')
|
|
403
425
|
return None
|
|
404
426
|
if not _missing and hasattr(sns, 'heatmap'):
|
|
@@ -442,6 +464,85 @@ def SB_heatmap(*a, **k):
|
|
|
442
464
|
return _safe_plot(lambda **kw: _mat_heat())
|
|
443
465
|
|
|
444
466
|
|
|
467
|
+
def viz_stacked_bar(df=None, x=None, hue=None, normalise=True, top_k=8):
|
|
468
|
+
"""
|
|
469
|
+
Stacked (optionally percentage-stacked) bar chart for two categorical columns.
|
|
470
|
+
|
|
471
|
+
- df: optional dataframe. If None, falls back to the active `df` via _pick_df().
|
|
472
|
+
- x: base categorical axis (e.g. 'state').
|
|
473
|
+
- hue: second categorical (e.g. 'body').
|
|
474
|
+
- normalise: if True, show percentages by x; else raw counts.
|
|
475
|
+
"""
|
|
476
|
+
from syntaxmatrix.display_html import show as _show
|
|
477
|
+
|
|
478
|
+
d = df if df is not None else _pick_df()
|
|
479
|
+
if d is None:
|
|
480
|
+
_show("⚠ Stacked bar skipped: no dataframe.")
|
|
481
|
+
return None
|
|
482
|
+
|
|
483
|
+
# Choose categorical candidates with reasonable cardinality
|
|
484
|
+
cat_cols = [
|
|
485
|
+
c for c in d.columns
|
|
486
|
+
if (d[c].dtype == "object" or str(d[c].dtype).startswith("category"))
|
|
487
|
+
and d[c].nunique(dropna=True) > 1
|
|
488
|
+
and d[c].nunique(dropna=True) <= 30
|
|
489
|
+
]
|
|
490
|
+
|
|
491
|
+
if x is None or x not in d.columns:
|
|
492
|
+
x = cat_cols[0] if cat_cols else None
|
|
493
|
+
if hue is None or hue not in d.columns:
|
|
494
|
+
remaining = [c for c in cat_cols if c != x]
|
|
495
|
+
hue = remaining[0] if remaining else None
|
|
496
|
+
|
|
497
|
+
if x is None or hue is None:
|
|
498
|
+
_show("⚠ Stacked bar skipped: need two categorical columns.")
|
|
499
|
+
return None
|
|
500
|
+
|
|
501
|
+
work = d[[x, hue]].dropna()
|
|
502
|
+
if work.empty:
|
|
503
|
+
_show("⚠ Stacked bar skipped: no data.")
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
def _draw():
|
|
507
|
+
_work = work.copy()
|
|
508
|
+
|
|
509
|
+
# Compress minor hue categories into "Other" for readability
|
|
510
|
+
keep_h = _work[hue].astype(str).value_counts().index[:top_k]
|
|
511
|
+
_work[hue] = _work[hue].astype(str).where(
|
|
512
|
+
_work[hue].astype(str).isin(keep_h),
|
|
513
|
+
other="Other",
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
tab = pd.crosstab(_work[x].astype(str), _work[hue].astype(str))
|
|
517
|
+
|
|
518
|
+
try:
|
|
519
|
+
_show(tab)
|
|
520
|
+
except Exception:
|
|
521
|
+
pass
|
|
522
|
+
|
|
523
|
+
plot_tab = tab.copy()
|
|
524
|
+
ylabel = "Count"
|
|
525
|
+
if normalise:
|
|
526
|
+
plot_tab = plot_tab.div(plot_tab.sum(axis=1), axis=0) * 100
|
|
527
|
+
ylabel = "Percentage"
|
|
528
|
+
|
|
529
|
+
ax = plot_tab.plot(kind="bar", stacked=True, figsize=(8, 4))
|
|
530
|
+
title = f"{hue} composition by {x}"
|
|
531
|
+
if normalise:
|
|
532
|
+
title += " (%)"
|
|
533
|
+
|
|
534
|
+
if not (ax.get_title() or "").strip():
|
|
535
|
+
ax.set_title(title)
|
|
536
|
+
ax.set_xlabel(str(x))
|
|
537
|
+
ax.set_ylabel(ylabel)
|
|
538
|
+
plt.xticks(rotation=45, ha="right")
|
|
539
|
+
plt.tight_layout()
|
|
540
|
+
return ax
|
|
541
|
+
|
|
542
|
+
# NOTE: _safe_plot handles empty plots and layout, but does NOT export PNGs.
|
|
543
|
+
return _safe_plot(lambda **kw: _draw())
|
|
544
|
+
|
|
545
|
+
|
|
445
546
|
def _safe_concat(objs, **kwargs):
|
|
446
547
|
import pandas as _pd
|
|
447
548
|
if objs is None:
|
|
@@ -453,23 +554,24 @@ def _safe_concat(objs, **kwargs):
|
|
|
453
554
|
except Exception as e:
|
|
454
555
|
smx_show(f'⚠ concat skipped: {e}')
|
|
455
556
|
return _pd.DataFrame()
|
|
456
|
-
|
|
557
|
+
|
|
457
558
|
|
|
458
559
|
def _SMX_OHE(**k):
|
|
459
560
|
# normalise arg name across sklearn versions
|
|
460
|
-
if
|
|
461
|
-
k[
|
|
462
|
-
k.setdefault(
|
|
463
|
-
k.setdefault(
|
|
561
|
+
if "sparse" in k and "sparse_output" not in k:
|
|
562
|
+
k["sparse_output"] = k.pop("sparse")
|
|
563
|
+
k.setdefault("handle_unknown", "ignore")
|
|
564
|
+
k.setdefault("sparse_output", False)
|
|
464
565
|
try:
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
566
|
+
if "sparse_output" not in inspect.signature(OneHotEncoder).parameters:
|
|
567
|
+
if "sparse_output" in k:
|
|
568
|
+
k["sparse"] = k.pop("sparse_output")
|
|
569
|
+
return OneHotEncoder(**k)
|
|
570
|
+
except TypeError:
|
|
571
|
+
if "sparse_output" in k:
|
|
572
|
+
k["sparse"] = k.pop("sparse_output")
|
|
573
|
+
return OneHotEncoder(**k)
|
|
574
|
+
|
|
473
575
|
|
|
474
576
|
def _SMX_mm(a, b):
|
|
475
577
|
try:
|
|
@@ -489,13 +591,32 @@ def _SMX_mm(a, b):
|
|
|
489
591
|
|
|
490
592
|
|
|
491
593
|
def _SMX_call(fn, *a, **k):
|
|
594
|
+
"""Safe metric invocation that can handle older sklearn signatures.
|
|
595
|
+
|
|
596
|
+
- If the metric accepts the provided keywords, it just runs.
|
|
597
|
+
- If we hit "unexpected keyword argument 'squared'", we drop that kw
|
|
598
|
+
and retry. When the caller asked for squared=False with
|
|
599
|
+
mean_squared_error, we emulate RMSE by taking the square root of
|
|
600
|
+
the returned MSE.
|
|
601
|
+
"""
|
|
602
|
+
squared_flag = k.get("squared", None)
|
|
492
603
|
try:
|
|
493
604
|
return fn(*a, **k)
|
|
494
605
|
except TypeError as e:
|
|
495
606
|
msg = str(e)
|
|
496
607
|
if "unexpected keyword argument 'squared'" in msg:
|
|
497
|
-
|
|
498
|
-
|
|
608
|
+
# remove unsupported kw and retry
|
|
609
|
+
k.pop("squared", None)
|
|
610
|
+
result = fn(*a, **k)
|
|
611
|
+
# emulate squared=False for old sklearn.mean_squared_error
|
|
612
|
+
try:
|
|
613
|
+
if squared_flag is False and getattr(fn, "__name__", "") == "mean_squared_error":
|
|
614
|
+
import numpy as _np
|
|
615
|
+
return float(_np.asarray(result, dtype=float) ** 0.5)
|
|
616
|
+
except Exception:
|
|
617
|
+
# if anything goes wrong, just fall back to the raw result
|
|
618
|
+
pass
|
|
619
|
+
return result
|
|
499
620
|
raise
|
|
500
621
|
|
|
501
622
|
|
syntaxmatrix/profiles.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from openai import OpenAI
|
|
3
3
|
from google import genai
|
|
4
4
|
import anthropic
|
|
5
|
-
|
|
5
|
+
from datetime import date
|
|
6
6
|
from syntaxmatrix.llm_store import list_profiles, load_profile
|
|
7
7
|
|
|
8
8
|
# Preload once at import-time
|
|
@@ -31,6 +31,7 @@ def get_profiles():
|
|
|
31
31
|
def get_client(profile):
|
|
32
32
|
|
|
33
33
|
provider = profile["provider"].lower()
|
|
34
|
+
model = profile["model"]
|
|
34
35
|
api_key = profile["api_key"]
|
|
35
36
|
|
|
36
37
|
#1 - Google - gemini series
|
|
@@ -41,25 +42,42 @@ def get_client(profile):
|
|
|
41
42
|
if provider == "openai":
|
|
42
43
|
return OpenAI(api_key=api_key)
|
|
43
44
|
|
|
44
|
-
#3 -
|
|
45
|
+
#3 - Anthropic claude series
|
|
46
|
+
if provider == "anthropic":
|
|
47
|
+
return anthropic.Anthropic(api_key=api_key)
|
|
48
|
+
|
|
49
|
+
#4 - xAI - grok series
|
|
45
50
|
if provider == "xai":
|
|
46
51
|
return OpenAI(api_key=api_key, base_url="https://api.x.ai/v1")
|
|
47
52
|
|
|
48
|
-
#
|
|
49
|
-
if provider == "deepseek":
|
|
50
|
-
|
|
53
|
+
#5 - DeepSeek chat model
|
|
54
|
+
if provider == "deepseek":
|
|
55
|
+
url_special = "https://api.deepseek.com/v3.2_speciale_expires_on_20251215"
|
|
56
|
+
url_default = "https://api.deepseek.com"
|
|
57
|
+
expiry_date = date(2025, 12, 15)
|
|
58
|
+
today = date.today()
|
|
59
|
+
if today < expiry_date:
|
|
60
|
+
base_url = url_special
|
|
61
|
+
else:
|
|
62
|
+
base_url = url_default
|
|
63
|
+
return OpenAI(api_key=api_key, base_url=base_url)
|
|
51
64
|
|
|
52
|
-
#
|
|
65
|
+
#6 - Moonshot chat model
|
|
53
66
|
if provider == "moonshot": #5
|
|
54
67
|
return OpenAI(api_key=api_key, base_url="https://api.moonshot.ai/v1")
|
|
55
68
|
|
|
56
|
-
#
|
|
69
|
+
#7 - Alibaba qwen series
|
|
57
70
|
if provider == "alibaba": #6
|
|
58
71
|
return OpenAI(api_key=api_key, base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",)
|
|
59
72
|
|
|
60
|
-
#
|
|
61
|
-
if provider == "
|
|
62
|
-
|
|
73
|
+
#8 - ZAI GLM series
|
|
74
|
+
if provider == "zai":
|
|
75
|
+
url = ""
|
|
76
|
+
if profile.get('name') == "coding":
|
|
77
|
+
url = "https://api.z.ai/api/coding/paas/v4"
|
|
78
|
+
else: url = "https://api.z.ai/api/paas/v4"
|
|
79
|
+
return OpenAI(api_key=api_key, base_url=url)
|
|
80
|
+
|
|
63
81
|
|
|
64
82
|
def drop_cached_profile_by_name(profile_name: str) -> bool:
|
|
65
83
|
"""
|