mindroot 9.19.0__py3-none-any.whl → 9.21.0__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.

Potentially problematic release.


This version of mindroot might be problematic. Click here for more details.

@@ -166,7 +166,7 @@ async def middleware(request: Request, call_next):
166
166
  status_code=403,
167
167
  content={"detail": "Invalid API key"}
168
168
  )
169
- if request.url.path.startswith("/imgs/"):
169
+ if request.url.path.startswith("/imgs/") or request.ur.startswith("/manual/"):
170
170
  return await call_next(request)
171
171
  try:
172
172
  path_parts = request.url.path.split('/')
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "Override Home Page example",
3
+ "version": "0.1.0",
4
+ "description": "Shows how to override the home page",
5
+ "services": [],
6
+ "commands": []
7
+ }
@@ -0,0 +1,32 @@
1
+ {# assume-blank #}
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <title>My Custom Home Page</title>
6
+ <style>
7
+
8
+ html, body, * {
9
+ color: white;
10
+ background: black;
11
+ font-family: 'Roboto', sans-serif;
12
+ }
13
+
14
+ body {
15
+ /* Need to center our message nicely */
16
+ display: flex;
17
+ justify-content: center;
18
+ align-items: center;
19
+ /* also align vertically in viewport */
20
+ height: 100vh;
21
+ margin: 0;
22
+ text-align: center;
23
+ }
24
+
25
+ </style>
26
+
27
+ </head>
28
+ <body>
29
+ All of the home page content blocks have been overridden.
30
+
31
+ </body>
32
+ </html>
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "Run Task example",
3
+ "version": "0.1.0",
4
+ "description": "Shows how to submit and wait for an agent to complete a task and display the result",
5
+ "services": [],
6
+ "commands": []
7
+ }
@@ -0,0 +1,73 @@
1
+ {# assume-blank #}
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Run Task</title>
8
+
9
+ <style>
10
+
11
+ html, body, * {
12
+ color: white;
13
+ background: black;
14
+ font-family: 'Roboto', sans-serif;
15
+ }
16
+
17
+ body {
18
+ margin: 20px;
19
+ padding: 20px;
20
+ }
21
+
22
+ .spinner {
23
+ border: 4px solid rgba(255, 255, 255, 0.3);
24
+ border-top: 4px solid white;
25
+ border-radius: 50%;
26
+ width: 24px;
27
+ height: 24px;
28
+ animation: spin 1s linear infinite;
29
+ }
30
+
31
+ @keyframes spin {
32
+ 0% { transform: rotate(0deg); }
33
+ 100% { transform: rotate(360deg); }
34
+ }
35
+
36
+ div, textarea, button {
37
+ display: block;
38
+ margin-top: 20px;
39
+ margin-bottom: 20px;
40
+ }
41
+
42
+ </style>
43
+
44
+ </head>
45
+ <body>
46
+ What should the agent display below?
47
+ <textarea id="task" rows="4" cols="50"></textarea>
48
+
49
+ <button id="go">Run Task</button>
50
+
51
+ <div id="spinner" class="spinner" style="display:none;"></div>
52
+
53
+ <div id="result"></div>
54
+
55
+ <script type="module">
56
+ import axios from 'https://cdn.skypack.dev/redaxios';
57
+ const $ = (id) => document.getElementById(id);
58
+
59
+ $('go').addEventListener('click', async () => {
60
+ const instructions = `Output ONLY html snippet to display "${$('task').value}" (be brief)`;
61
+ try {
62
+ $('spinner').style.display = 'block';
63
+ const {data: {results}} = await axios.post('/task/Assistant', { instructions });
64
+ $('spinner').style.display = 'none';
65
+ $('result').innerHTML = results;
66
+ } catch (error) {
67
+ alert('Error: ' + error.message);
68
+ }
69
+ });
70
+
71
+ </script>
72
+ </body>
73
+ </html>
mindroot/lib/templates.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import logging
3
3
  from jinja2 import Environment, FileSystemLoader, ChoiceLoader
4
+ import re
4
5
  from .plugins import list_enabled, get_plugin_path
5
6
  from .parent_templates import get_parent_templates_env
6
7
  import traceback
@@ -227,6 +228,7 @@ async def find_parent_template(page_name, plugins):
227
228
  return rel_path
228
229
  return alt_path
229
230
  return None
231
+
230
232
  async def find_plugin_template(page_name, plugins):
231
233
  """Find a template in a plugin's templates directory.
232
234
 
@@ -306,9 +308,6 @@ async def load_plugin_templates(page_name, plugins):
306
308
  #print(f"Found inject template at: {path}")
307
309
  templates.append({'type': 'inject', 'template': env.from_string(content)})
308
310
  break
309
- #else:
310
- #
311
- # print(f"Inject template not found at: {path}")
312
311
 
313
312
  # Check override templates
314
313
  override_paths = [
@@ -323,8 +322,16 @@ async def load_plugin_templates(page_name, plugins):
323
322
  # Load template content with translation support
324
323
  content = load_template_with_translation(path)
325
324
  if content:
326
- #print(f"Found override template at: {path}")
327
- templates.append({'type': 'override', 'template': env.from_string(content)})
325
+ # Detect top-of-file pragma to assume unspecified blocks are blank
326
+ # Accepted forms (case-insensitive):
327
+ # {# assume-blank #}, {# assume_blank #}, {# assume-blank: true #}, {# assume_blank: true #}
328
+ assume_blank = False
329
+ head = content[:2048]
330
+ pragma_re = re.compile(r"^\s*\{#\s*(assume[-_]blank)(?:\s*true)?\s*#\}\s*", re.IGNORECASE | re.MULTILINE)
331
+ if pragma_re.search(head):
332
+ assume_blank = True
333
+ content = pragma_re.sub("", content, count=1)
334
+ templates.append({'type': 'override', 'template': env.from_string(content), 'assume_blank': assume_blank})
328
335
  break
329
336
 
330
337
  except Exception as e:
@@ -332,7 +339,7 @@ async def load_plugin_templates(page_name, plugins):
332
339
  continue
333
340
  return templates
334
341
 
335
- async def collect_content(template, blocks, template_type, data):
342
+ async def collect_content(template, blocks, template_type, data, assume_blank=False):
336
343
  """Collect content from child templates.
337
344
 
338
345
  Args:
@@ -340,19 +347,30 @@ async def collect_content(template, blocks, template_type, data):
340
347
  blocks (list): List of block names
341
348
  template_type (str): Type of template ('inject' or 'override')
342
349
  data (dict): Template context data
350
+ assume_blank (bool): If True and type is 'override', unspecified blocks are blanked
343
351
 
344
352
  Returns:
345
353
  dict: Collected content by block
346
354
  """
347
355
  content = {block: {'inject': [], 'override': None} for block in blocks}
356
+
357
+ # Get blocks that are actually defined in this template
358
+ defined_blocks = set(template.blocks.keys())
359
+
348
360
  for block in blocks:
349
- if block in template.blocks:
361
+ if block in defined_blocks:
362
+ # Block is explicitly defined in the template
350
363
  block_content = ''.join(template.blocks[block](template.new_context(data)))
351
-
364
+
352
365
  if template_type == 'override':
353
366
  content[block]['override'] = block_content
354
367
  else:
355
368
  content[block]['inject'].append(block_content)
369
+ elif template_type == 'override' and assume_blank:
370
+ # Block is NOT defined in override template, but assume_blank is True
371
+ # So we treat it as if it was defined but empty
372
+ content[block]['override'] = ''
373
+
356
374
  return content
357
375
 
358
376
  async def render_combined_template(page_name, plugins, context):
@@ -366,6 +384,35 @@ async def render_combined_template(page_name, plugins, context):
366
384
  Returns:
367
385
  str: Rendered HTML
368
386
  """
387
+ # Check for assume-blank pragma and render directly if found
388
+ for plugin in plugins:
389
+ plugin_path = get_plugin_path(plugin)
390
+ if not plugin_path:
391
+ continue
392
+
393
+ last_part = plugin_path.split('/')[-1]
394
+ override_paths = [
395
+ os.path.join(plugin_path, 'override', f'{page_name}.jinja2'),
396
+ os.path.join(plugin_path, 'src', plugin, 'override', f'{page_name}.jinja2'),
397
+ os.path.join(plugin_path, 'src', 'override', f'{page_name}.jinja2'),
398
+ os.path.join(plugin_path, 'src', last_part, 'override', f'{page_name}.jinja2')
399
+ ]
400
+
401
+ for path in override_paths:
402
+ if os.path.exists(path):
403
+ content = load_template_with_translation(path)
404
+ if content:
405
+ # Check for assume-blank pragma
406
+ head = content[:2048]
407
+ pragma_re = re.compile(r"^\s*\{#\s*(assume[-_]blank)(?:\s*true)?\s*#\}\s*", re.IGNORECASE | re.MULTILINE)
408
+ if pragma_re.search(head):
409
+ # Found assume-blank! Just render this template directly
410
+ print(f"Found assume-blank pragma in {path}, rendering directly without parent")
411
+ content = pragma_re.sub("", content, count=1) # Remove pragma
412
+ template = env.from_string(content)
413
+ return template.render(**context) # Return early!
414
+
415
+ # If no assume-blank pragma found, continue with normal processing
369
416
  # Load parent template with translation support
370
417
  parent_template = None
371
418
  parent_template_path = None
@@ -410,21 +457,23 @@ async def render_combined_template(page_name, plugins, context):
410
457
 
411
458
  for child_template_info in child_templates:
412
459
  print("calling collect_content")
460
+ # Pass assume_blank flag to collect_content
413
461
  child_content = await collect_content(
414
462
  child_template_info['template'],
415
463
  parent_blocks,
416
464
  child_template_info['type'],
417
- context
465
+ context,
466
+ child_template_info.get('assume_blank', False)
418
467
  )
419
468
  for block, content in child_content.items():
420
- if content['override']:
469
+ if content['override'] is not None: # Check for None specifically, empty string is valid
421
470
  all_content[block]['override'] = content['override']
422
471
  else:
423
472
  all_content[block]['inject'].extend(content['inject'])
424
473
 
425
474
  combined_template_str = '{% extends layout_template %}\n'
426
475
  for block in all_content:
427
- if all_content[block]['override']:
476
+ if all_content[block]['override'] is not None: # Check for None, empty string is valid override
428
477
  combined_template_str += f'{{% block {block} %}}\n {{{{ combined_{block}_override|safe }}}}\n{{% endblock %}}\n'
429
478
  else:
430
479
  combined_template_str += f'{{% block {block} %}}\n {{{{ super() }}}}\n {{{{ combined_{block}_inject|safe }}}}\n{{% endblock %}}\n'
@@ -446,7 +495,7 @@ async def render_combined_template(page_name, plugins, context):
446
495
  else:
447
496
  combined_inject[f'combined_{block}_inject'] = ''
448
497
 
449
- if 'override' in content and content['override']:
498
+ if 'override' in content and content['override'] is not None:
450
499
  combined_override[f'combined_{block}_override'] = content['override']
451
500
 
452
501
  print("in render combined, context is", context)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mindroot
3
- Version: 9.19.0
3
+ Version: 9.21.0
4
4
  Summary: MindRoot AI Agent Framework
5
5
  Requires-Python: >=3.9
6
6
  License-File: LICENSE
@@ -1702,7 +1702,7 @@ mindroot/coreplugins/index/static/js/lit-html/node/directives/until.js.map,sha25
1702
1702
  mindroot/coreplugins/index/static/js/lit-html/node/directives/when.js,sha256=NLe0NJ-6jqjVDUrT_DzmSpREsRaLo1yarzdYcV_5xHY,181
1703
1703
  mindroot/coreplugins/index/static/js/lit-html/node/directives/when.js.map,sha256=tOonih_-EaqrunhNGshA9xN--WIVdGikjg8MkVp0itQ,1534
1704
1704
  mindroot/coreplugins/jwt_auth/__init__.py,sha256=qFCBnx0oAKTtMSXiPEa7VXOIlWDTU-5CY0XvodgSUlM,79
1705
- mindroot/coreplugins/jwt_auth/middleware.py,sha256=aOrKIQMDzLQArlizkGC3aRmUcPQuZRfKFUaceAgIYm8,10880
1705
+ mindroot/coreplugins/jwt_auth/middleware.py,sha256=Qo_VFxASF2cmgL7OFPtBMYFBYhFVRnqBlipad14uYf0,10917
1706
1706
  mindroot/coreplugins/jwt_auth/mod.py,sha256=XqvAwBQVga-foEkziDJnQtISq1NwYcXXuvVRKeewfAQ,2070
1707
1707
  mindroot/coreplugins/jwt_auth/role_checks.py,sha256=bruZIIBSOvXNWB1YZ2s5btrbbXNf18w6MdORpJByV60,1555
1708
1708
  mindroot/coreplugins/jwt_auth/router.py,sha256=ecXYao_UG33UjQF15Hi-tf_X0eFsqLEldyqGpt7JNSw,1162
@@ -2175,6 +2175,11 @@ mindroot/docs/data/providers.json,sha256=FPmY5qVOrBy_Z4RgDJWQwLwxd-zWWI83nnAE6z5
2175
2175
  mindroot/docs/data/agents/local/Assistant/agent.json,sha256=P4CaQpQaUTwx0PoyV9bCJHvfvANsFyBZlNcMtVlxM3M,1281
2176
2176
  mindroot/docs/data/agents/local/SysAdmin/agent.json,sha256=wkZvJbqM153Gr0cnt531F_XndV5DXt6dkkYnXH6x91c,2864
2177
2177
  mindroot/docs/data/mcp/servers.json,sha256=UrHgExs_P05yehMPSa-FDSGHmjCmYABJ5BdW0_SOXXY,174
2178
+ mindroot/docs/example_plugins/override_home/plugin_info.json,sha256=z0k6KgqfMbN5Oagfq0-PemvdsM91lHIFahL756_0jcY,157
2179
+ mindroot/docs/example_plugins/override_home/src/override_home/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
2180
+ mindroot/docs/example_plugins/override_home/src/override_home/override/home.jinja2,sha256=emyKqeNUxSH8YGUU4iLQM7xOBrJ4PvnZXgZ4lS7iw0s,513
2181
+ mindroot/docs/example_plugins/run_task/plugin_info.json,sha256=RxeUhDKG0M23MaxNmwiqE_NFjrohp8k8cxQBHfIhujM,195
2182
+ mindroot/docs/example_plugins/run_task/src/run_task/override/home.jinja2,sha256=AovNqm8NeUbZcGjldjaxqg-HqTfq7cHrE_ugYLevXCc,1535
2178
2183
  mindroot/docs/personas/local/Assistant/avatar.png,sha256=AsT2_jjGpZvEhzTEwSVhEShSYaIBhcUDrlj_bAG_HVY,1169266
2179
2184
  mindroot/docs/personas/local/Assistant/faceref.png,sha256=AsT2_jjGpZvEhzTEwSVhEShSYaIBhcUDrlj_bAG_HVY,1169266
2180
2185
  mindroot/docs/personas/local/Assistant/persona.json,sha256=FFOTyWMozCPM62Dn7MXwZUJIoNRkKbEzvpH8q_YTDgs,437
@@ -2224,7 +2229,7 @@ mindroot/lib/plugins_install.py,sha256=CUDkdDhIKkwN3U1hfKaTq8QUwGApFrNKmt3iycZuT
2224
2229
  mindroot/lib/route_decorators.py,sha256=d0sE6BnEolmSuwe3khenDmUiyeiPklNuaMFE6roH6sk,2206
2225
2230
  mindroot/lib/session_files.py,sha256=yY8TKfJPGZoK4yAy3WbfrJ7I5xL6NfDJmIICH6qC7Bw,1167
2226
2231
  mindroot/lib/streamcmd.py,sha256=f9n3OtryEkMbNNuFr5BAZn1EpSLUKuDZw-zpo97XxJk,4714
2227
- mindroot/lib/templates.py,sha256=vx8-2aqzkwPGj72vyHhRndspvyaEP2bE8OYRd5i_d64,22178
2232
+ mindroot/lib/templates.py,sha256=JZZiEuD3jLeiXR1k8IUzBPolIcZ16Xb-dKdTYSIqHNw,25049
2228
2233
  mindroot/lib/token_counter.py,sha256=6k6nq2oCjJ4XiWVp89vYEdMTmfRb4ejEcpjK-vXcI6w,7607
2229
2234
  mindroot/lib/auth/__init__.py,sha256=5EZbCTcdlnTHYE0JNk8znWNSO7mOsokMOvRBjb5Mq-M,49
2230
2235
  mindroot/lib/auth/api_key.py,sha256=jlihhB94rsNVcXS-Gqmh7ADMwUbgROA84zPW7aOkfJw,875
@@ -2268,9 +2273,9 @@ mindroot/protocols/services/stream_chat.py,sha256=fMnPfwaB5fdNMBLTEg8BXKAGvrELKH
2268
2273
  mindroot/registry/__init__.py,sha256=40Xy9bmPHsgdIrOzbtBGzf4XMqXVi9P8oZTJhn0r654,151
2269
2274
  mindroot/registry/component_manager.py,sha256=WZFNPg4SNvpqsM5NFiC2DpgmrJQCyR9cNhrCBpp30Qk,995
2270
2275
  mindroot/registry/data_access.py,sha256=81In5TwETpaqnnY1_-tBQM7rfWvUxZUZkG7lEelRUfU,5321
2271
- mindroot-9.19.0.dist-info/licenses/LICENSE,sha256=8plAmZh8y9ccuuqFFz4kp7G-cO_qsPgAOoHNvabSB4U,1070
2272
- mindroot-9.19.0.dist-info/METADATA,sha256=vabnW_ilYBbCGufx0OvuaxhsQLVpOntyOmjdzFuOk34,1035
2273
- mindroot-9.19.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2274
- mindroot-9.19.0.dist-info/entry_points.txt,sha256=0bpyjMccLttx6VcjDp6zfJPN0Kk0rffor6IdIbP0j4c,50
2275
- mindroot-9.19.0.dist-info/top_level.txt,sha256=gwKm7DmNjhdrCJTYCrxa9Szne4lLpCtrEBltfsX-Mm8,9
2276
- mindroot-9.19.0.dist-info/RECORD,,
2276
+ mindroot-9.21.0.dist-info/licenses/LICENSE,sha256=8plAmZh8y9ccuuqFFz4kp7G-cO_qsPgAOoHNvabSB4U,1070
2277
+ mindroot-9.21.0.dist-info/METADATA,sha256=mjUORZGKFy7pobyw5GmSCad2UfOM0kuqJDjmuel3OUc,1035
2278
+ mindroot-9.21.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2279
+ mindroot-9.21.0.dist-info/entry_points.txt,sha256=0bpyjMccLttx6VcjDp6zfJPN0Kk0rffor6IdIbP0j4c,50
2280
+ mindroot-9.21.0.dist-info/top_level.txt,sha256=gwKm7DmNjhdrCJTYCrxa9Szne4lLpCtrEBltfsX-Mm8,9
2281
+ mindroot-9.21.0.dist-info/RECORD,,