llms-py 3.0.0b7__py3-none-any.whl → 3.0.0b8__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.
- llms/__pycache__/main.cpython-314.pyc +0 -0
- llms/extensions/analytics/ui/index.mjs +51 -162
- llms/extensions/app/__init__.py +519 -0
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/db.py +641 -0
- llms/extensions/app/db_manager.py +195 -0
- llms/extensions/app/requests.json +9073 -0
- llms/extensions/app/threads.json +15290 -0
- llms/{ui/modules/threads → extensions/app/ui}/Recents.mjs +82 -55
- llms/{ui/modules/threads → extensions/app/ui}/index.mjs +78 -9
- llms/extensions/app/ui/threadStore.mjs +407 -0
- llms/extensions/core_tools/__init__.py +272 -32
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +9884 -0
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms/extensions/core_tools/ui/index.mjs +650 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/gallery/db.py +4 -4
- llms/extensions/gallery/ui/index.mjs +2 -1
- llms/extensions/katex/__init__.py +6 -0
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/katex/ui/README.md +125 -0
- llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +92 -0
- llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms/extensions/katex/ui/katex.css +1230 -0
- llms/extensions/katex/ui/katex.js +19080 -0
- llms/extensions/katex/ui/katex.min.css +1 -0
- llms/extensions/katex/ui/katex.min.js +1 -0
- llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms/extensions/katex/ui/katex.mjs +18547 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/anthropic.py +44 -1
- llms/extensions/system_prompts/ui/index.mjs +2 -1
- llms/extensions/tools/__init__.py +5 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +8 -8
- llms/index.html +26 -38
- llms/llms.json +4 -1
- llms/main.py +492 -103
- llms/ui/App.mjs +2 -3
- llms/ui/ai.mjs +29 -13
- llms/ui/app.css +250 -398
- llms/ui/ctx.mjs +84 -6
- llms/ui/index.mjs +4 -6
- llms/ui/lib/vue.min.mjs +10 -9
- llms/ui/lib/vue.mjs +1796 -1635
- llms/ui/markdown.mjs +4 -2
- llms/ui/modules/chat/ChatBody.mjs +90 -86
- llms/ui/modules/chat/HomeTools.mjs +0 -242
- llms/ui/modules/chat/index.mjs +103 -170
- llms/ui/modules/model-selector.mjs +2 -2
- llms/ui/tailwind.input.css +35 -1
- llms/ui/utils.mjs +12 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/METADATA +1 -1
- llms_py-3.0.0b8.dist-info/RECORD +198 -0
- llms/ui/modules/threads/threadStore.mjs +0 -640
- llms_py-3.0.0b7.dist-info/RECORD +0 -80
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b8.dist-info}/top_level.txt +0 -0
|
@@ -3,11 +3,13 @@ Core System Tools providing essential file operations, memory persistence, math
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import ast
|
|
6
|
+
import contextlib
|
|
6
7
|
import glob
|
|
7
8
|
import json
|
|
8
9
|
import math
|
|
9
10
|
import operator
|
|
10
11
|
import os
|
|
12
|
+
import shutil
|
|
11
13
|
import subprocess
|
|
12
14
|
import sys
|
|
13
15
|
import tempfile
|
|
@@ -15,6 +17,10 @@ from datetime import datetime, timezone
|
|
|
15
17
|
from statistics import mean, median, stdev, variance
|
|
16
18
|
from typing import Any, Dict, List, Optional
|
|
17
19
|
|
|
20
|
+
from aiohttp import web
|
|
21
|
+
|
|
22
|
+
g_ctx = None
|
|
23
|
+
|
|
18
24
|
# -----------------------------
|
|
19
25
|
# In-memory storage (replace later)
|
|
20
26
|
# -----------------------------
|
|
@@ -179,6 +185,26 @@ def glob_paths(
|
|
|
179
185
|
# -----------------------------
|
|
180
186
|
|
|
181
187
|
|
|
188
|
+
def get_calculator_functions():
|
|
189
|
+
# 2. Define allowed math functions and constants
|
|
190
|
+
allowed_functions = {
|
|
191
|
+
"mod": operator.mod,
|
|
192
|
+
"mean": mean,
|
|
193
|
+
"median": median,
|
|
194
|
+
"stdev": stdev,
|
|
195
|
+
"variance": variance,
|
|
196
|
+
"abs": abs,
|
|
197
|
+
"min": min,
|
|
198
|
+
"max": max,
|
|
199
|
+
"sum": sum,
|
|
200
|
+
"round": round,
|
|
201
|
+
}
|
|
202
|
+
allowed_functions.update(
|
|
203
|
+
{name: getattr(math, name) for name in dir(math) if not name.startswith("_") and name not in allowed_functions}
|
|
204
|
+
)
|
|
205
|
+
return allowed_functions
|
|
206
|
+
|
|
207
|
+
|
|
182
208
|
def calc(expression: str) -> str:
|
|
183
209
|
"""Evaluate a mathematical expression with boolean operations"""
|
|
184
210
|
# 1. Define allowed operators
|
|
@@ -204,68 +230,87 @@ def calc(expression: str) -> str:
|
|
|
204
230
|
}
|
|
205
231
|
|
|
206
232
|
# 2. Define allowed math functions and constants
|
|
207
|
-
allowed_functions =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
"median": median,
|
|
213
|
-
"stdev": stdev,
|
|
214
|
-
"variance": variance,
|
|
215
|
-
"abs": abs,
|
|
216
|
-
"min": min,
|
|
217
|
-
"max": max,
|
|
218
|
-
"sum": sum,
|
|
219
|
-
"round": round,
|
|
220
|
-
}
|
|
221
|
-
)
|
|
233
|
+
allowed_functions = get_calculator_functions()
|
|
234
|
+
|
|
235
|
+
def eval_node(node, context=None):
|
|
236
|
+
if context is None:
|
|
237
|
+
context = {}
|
|
222
238
|
|
|
223
|
-
def eval_node(node):
|
|
224
239
|
if isinstance(node, ast.Constant): # Numbers and booleans
|
|
225
240
|
return node.value
|
|
226
241
|
elif isinstance(node, ast.BinOp): # Binary Ops (1 + 2)
|
|
227
|
-
return operators[type(node.op)](eval_node(node.left), eval_node(node.right))
|
|
242
|
+
return operators[type(node.op)](eval_node(node.left, context), eval_node(node.right, context))
|
|
228
243
|
elif isinstance(node, ast.UnaryOp): # Unary Ops (-5, not True)
|
|
229
|
-
return operators[type(node.op)](eval_node(node.operand))
|
|
244
|
+
return operators[type(node.op)](eval_node(node.operand, context))
|
|
230
245
|
elif isinstance(node, ast.Compare): # Comparison (5 > 3)
|
|
231
|
-
left = eval_node(node.left)
|
|
246
|
+
left = eval_node(node.left, context)
|
|
232
247
|
for op, comparator in zip(node.ops, node.comparators):
|
|
233
|
-
right = eval_node(comparator)
|
|
248
|
+
right = eval_node(comparator, context)
|
|
234
249
|
if not operators[type(op)](left, right):
|
|
235
250
|
return False
|
|
236
251
|
left = right
|
|
237
252
|
return True
|
|
238
253
|
elif isinstance(node, ast.BoolOp): # Boolean operations (True and False, True or False)
|
|
239
|
-
op = operators[type(node.op)]
|
|
240
254
|
if isinstance(node.op, ast.And):
|
|
241
255
|
# Short-circuit evaluation for 'and'
|
|
242
256
|
result = True
|
|
243
257
|
for value in node.values:
|
|
244
|
-
result = eval_node(value)
|
|
258
|
+
result = eval_node(value, context)
|
|
245
259
|
if not result:
|
|
246
260
|
return False
|
|
247
261
|
return result
|
|
248
262
|
elif isinstance(node.op, ast.Or):
|
|
249
263
|
# Short-circuit evaluation for 'or'
|
|
250
264
|
for value in node.values:
|
|
251
|
-
result = eval_node(value)
|
|
265
|
+
result = eval_node(value, context)
|
|
252
266
|
if result:
|
|
253
267
|
return True
|
|
254
268
|
return False
|
|
255
269
|
elif isinstance(node, ast.Call): # Function calls (sqrt(16))
|
|
256
270
|
func_name = node.func.id
|
|
257
271
|
if func_name in allowed_functions:
|
|
258
|
-
args = [eval_node(arg) for arg in node.args]
|
|
272
|
+
args = [eval_node(arg, context) for arg in node.args]
|
|
259
273
|
return allowed_functions[func_name](*args)
|
|
274
|
+
if func_name == "range":
|
|
275
|
+
args = [eval_node(arg, context) for arg in node.args]
|
|
276
|
+
return range(*args)
|
|
260
277
|
raise NameError(f"Function '{func_name}' is not allowed.")
|
|
261
|
-
elif isinstance(node, ast.Name): # Constants (pi, e, True, False)
|
|
278
|
+
elif isinstance(node, ast.Name): # Constants (pi, e, True, False) or context variables
|
|
279
|
+
if node.id in context:
|
|
280
|
+
return context[node.id]
|
|
262
281
|
if node.id in allowed_functions:
|
|
263
282
|
return allowed_functions[node.id]
|
|
264
283
|
elif node.id in ("True", "False"):
|
|
265
284
|
return node.id == "True"
|
|
266
285
|
raise NameError(f"Variable '{node.id}' is not defined.")
|
|
267
286
|
elif isinstance(node, ast.List): # List literals [1, 2, 3]
|
|
268
|
-
return [eval_node(item) for item in node.elts]
|
|
287
|
+
return [eval_node(item, context) for item in node.elts]
|
|
288
|
+
elif isinstance(node, ast.ListComp): # List comprehensions [x*2 for x in [1,2,3]]
|
|
289
|
+
result = []
|
|
290
|
+
generators = node.generators
|
|
291
|
+
if len(generators) != 1:
|
|
292
|
+
raise ValueError("Only single-generator list comprehensions are supported")
|
|
293
|
+
gen = generators[0]
|
|
294
|
+
if not isinstance(gen.target, ast.Name):
|
|
295
|
+
raise ValueError("Only simple name targets in list comprehensions are supported")
|
|
296
|
+
|
|
297
|
+
target_name = gen.target.id
|
|
298
|
+
iterable = eval_node(gen.iter, context)
|
|
299
|
+
|
|
300
|
+
for item in iterable:
|
|
301
|
+
new_context = context.copy()
|
|
302
|
+
new_context[target_name] = item
|
|
303
|
+
|
|
304
|
+
# Check ifs
|
|
305
|
+
include = True
|
|
306
|
+
for if_node in gen.ifs:
|
|
307
|
+
if not eval_node(if_node, new_context):
|
|
308
|
+
include = False
|
|
309
|
+
break
|
|
310
|
+
|
|
311
|
+
if include:
|
|
312
|
+
result.append(eval_node(node.elt, new_context))
|
|
313
|
+
return result
|
|
269
314
|
else:
|
|
270
315
|
raise TypeError(f"Unsupported operation: {type(node).__name__}")
|
|
271
316
|
|
|
@@ -274,13 +319,19 @@ def calc(expression: str) -> str:
|
|
|
274
319
|
|
|
275
320
|
# Parse and evaluate
|
|
276
321
|
node = ast.parse(expression, mode="eval").body
|
|
277
|
-
|
|
322
|
+
ret = eval_node(node)
|
|
323
|
+
g_ctx.dbg(f"calc ({expression}) = {ret}")
|
|
324
|
+
return ret
|
|
278
325
|
|
|
279
326
|
|
|
280
327
|
# -----------------------------
|
|
281
|
-
#
|
|
328
|
+
# code execution tools
|
|
282
329
|
# -----------------------------
|
|
283
330
|
|
|
331
|
+
mem_limit = 8589934592 # Max virtual memory 8GB
|
|
332
|
+
cpu_time_limit = 5 # Max CPU time 5 seconds
|
|
333
|
+
resource_limits = f"ulimit -t {cpu_time_limit}; ulimit -v {mem_limit};"
|
|
334
|
+
|
|
284
335
|
|
|
285
336
|
def run_python(code: str) -> Dict[str, Any]:
|
|
286
337
|
"""
|
|
@@ -293,16 +344,141 @@ def run_python(code: str) -> Dict[str, Any]:
|
|
|
293
344
|
with open(script_path, "w", encoding="utf-8") as f:
|
|
294
345
|
f.write(code)
|
|
295
346
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
347
|
+
cmd = f"{resource_limits} {sys.executable} script.py"
|
|
348
|
+
|
|
349
|
+
run_as = os.environ.get("LLMS_RUN_AS")
|
|
350
|
+
if run_as:
|
|
351
|
+
# Grant access to temp_dir
|
|
352
|
+
with contextlib.suppress(Exception):
|
|
353
|
+
os.chmod(temp_dir, 0o777)
|
|
354
|
+
cmd = f"sudo -u {run_as} bash -c '{cmd}'"
|
|
300
355
|
|
|
301
356
|
try:
|
|
302
357
|
# Run with restricted environment
|
|
303
358
|
# We keep PATH to find basic tools if needed, but remove sensitive vars
|
|
304
359
|
clean_env = {"PATH": os.environ.get("PATH", "")}
|
|
305
360
|
|
|
361
|
+
g_ctx.dbg(f"run_python ({temp_dir}): {cmd}\n{code}")
|
|
362
|
+
result = subprocess.run(
|
|
363
|
+
["bash", "-c", cmd], cwd=temp_dir, env=clean_env, capture_output=True, text=True, timeout=10
|
|
364
|
+
)
|
|
365
|
+
return {"stdout": result.stdout, "stderr": result.stderr, "returncode": result.returncode}
|
|
366
|
+
except subprocess.TimeoutExpired:
|
|
367
|
+
return {"stdout": "", "stderr": "Execution timed out", "returncode": -1}
|
|
368
|
+
except Exception as e:
|
|
369
|
+
return {"stdout": "", "stderr": f"Error: {e}", "returncode": -1}
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def run_javascript(code: str) -> Dict[str, Any]:
|
|
373
|
+
"""
|
|
374
|
+
Execute JavaScript code in a temporary sandboxed environment using bun or node.
|
|
375
|
+
"""
|
|
376
|
+
# Check for available runtime
|
|
377
|
+
runtime = shutil.which("bun") or shutil.which("node")
|
|
378
|
+
if not runtime:
|
|
379
|
+
return {"stdout": "", "stderr": "Error: Neither 'bun' nor 'node' is available on the system.", "returncode": -1}
|
|
380
|
+
|
|
381
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
382
|
+
script_path = os.path.join(temp_dir, "script.js")
|
|
383
|
+
|
|
384
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
385
|
+
f.write(code)
|
|
386
|
+
|
|
387
|
+
cmd = f"{resource_limits} {runtime} script.js"
|
|
388
|
+
|
|
389
|
+
run_as = os.environ.get("LLMS_RUN_AS")
|
|
390
|
+
if run_as:
|
|
391
|
+
with contextlib.suppress(Exception):
|
|
392
|
+
os.chmod(temp_dir, 0o777)
|
|
393
|
+
cmd = f"sudo -u {run_as} bash -c '{cmd}'"
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
# Run with restricted environment
|
|
397
|
+
clean_env = {"PATH": os.environ.get("PATH", "")}
|
|
398
|
+
|
|
399
|
+
g_ctx.dbg(f"run_javascript ({temp_dir}): {cmd}\n{code}")
|
|
400
|
+
result = subprocess.run(
|
|
401
|
+
["bash", "-c", cmd], cwd=temp_dir, env=clean_env, capture_output=True, text=True, timeout=10
|
|
402
|
+
)
|
|
403
|
+
return {"stdout": result.stdout, "stderr": result.stderr, "returncode": result.returncode}
|
|
404
|
+
except subprocess.TimeoutExpired:
|
|
405
|
+
return {"stdout": "", "stderr": "Execution timed out", "returncode": -1}
|
|
406
|
+
except Exception as e:
|
|
407
|
+
return {"stdout": "", "stderr": f"Error: {e}", "returncode": -1}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def run_typescript(code: str) -> Dict[str, Any]:
|
|
411
|
+
"""
|
|
412
|
+
Execute TypeScript code in a temporary sandboxed environment using bun or node.
|
|
413
|
+
"""
|
|
414
|
+
# Check for available runtime
|
|
415
|
+
runtime = shutil.which("bun") or shutil.which("node")
|
|
416
|
+
if not runtime:
|
|
417
|
+
return {"stdout": "", "stderr": "Error: Neither 'bun' nor 'node' is available on the system.", "returncode": -1}
|
|
418
|
+
|
|
419
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
420
|
+
script_path = os.path.join(temp_dir, "script.ts")
|
|
421
|
+
|
|
422
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
423
|
+
f.write(code)
|
|
424
|
+
|
|
425
|
+
cmd = f"{resource_limits} {runtime} script.ts"
|
|
426
|
+
|
|
427
|
+
run_as = os.environ.get("LLMS_RUN_AS")
|
|
428
|
+
if run_as:
|
|
429
|
+
with contextlib.suppress(Exception):
|
|
430
|
+
os.chmod(temp_dir, 0o777)
|
|
431
|
+
cmd = f"sudo -u {run_as} bash -c '{cmd}'"
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
# Run with restricted environment
|
|
435
|
+
clean_env = {"PATH": os.environ.get("PATH", "")}
|
|
436
|
+
|
|
437
|
+
g_ctx.dbg(f"run_typescript ({temp_dir}): {cmd}\n{code}")
|
|
438
|
+
result = subprocess.run(
|
|
439
|
+
["bash", "-c", cmd], cwd=temp_dir, env=clean_env, capture_output=True, text=True, timeout=10
|
|
440
|
+
)
|
|
441
|
+
return {"stdout": result.stdout, "stderr": result.stderr, "returncode": result.returncode}
|
|
442
|
+
except subprocess.TimeoutExpired:
|
|
443
|
+
return {"stdout": "", "stderr": "Execution timed out", "returncode": -1}
|
|
444
|
+
except Exception as e:
|
|
445
|
+
return {"stdout": "", "stderr": f"Error: {e}", "returncode": -1}
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def run_csharp(code: str) -> Dict[str, Any]:
|
|
449
|
+
"""
|
|
450
|
+
Execute C# code in a temporary sandboxed environment using dotnet.
|
|
451
|
+
"""
|
|
452
|
+
# Check for available runtime
|
|
453
|
+
runtime = shutil.which("dotnet")
|
|
454
|
+
if not runtime:
|
|
455
|
+
return {"stdout": "", "stderr": "Error: 'dotnet' is not available on the system.", "returncode": -1}
|
|
456
|
+
|
|
457
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
458
|
+
script_path = os.path.join(temp_dir, "script.cs")
|
|
459
|
+
|
|
460
|
+
# Ensure we just have the code, user might pass it without wrapping class if it's top-level statements
|
|
461
|
+
with open(script_path, "w", encoding="utf-8") as f:
|
|
462
|
+
f.write(code)
|
|
463
|
+
|
|
464
|
+
# Note: 'dotnet run script.cs' is the command as per user request for .NET 10
|
|
465
|
+
cmd = f"{resource_limits} {runtime} run script.cs"
|
|
466
|
+
|
|
467
|
+
run_as = os.environ.get("LLMS_RUN_AS")
|
|
468
|
+
if run_as:
|
|
469
|
+
with contextlib.suppress(Exception):
|
|
470
|
+
os.chmod(temp_dir, 0o777)
|
|
471
|
+
# For dotnet, we need to set HOME and DOTNET_CLI_HOME to temp_dir for write access
|
|
472
|
+
cmd = f"sudo -u {run_as} env HOME={temp_dir} DOTNET_CLI_HOME={temp_dir} bash -c '{cmd}'"
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
# Run with restricted environment
|
|
476
|
+
clean_env = {"PATH": os.environ.get("PATH", "")}
|
|
477
|
+
|
|
478
|
+
# Dotnet might need some ENV vars to work correctly, usually DOTNET_CLI_HOME or similar if strictly sandboxed
|
|
479
|
+
# But we are keeping PATH, hopefully commonly needed vars are there or default works.
|
|
480
|
+
# We might want to pass more env vars if it fails.
|
|
481
|
+
g_ctx.dbg(f"run_csharp ({temp_dir}): {cmd}\n{code}")
|
|
306
482
|
result = subprocess.run(
|
|
307
483
|
["bash", "-c", cmd], cwd=temp_dir, env=clean_env, capture_output=True, text=True, timeout=10
|
|
308
484
|
)
|
|
@@ -342,6 +518,8 @@ def get_current_time(tz_name: Optional[str] = None) -> str:
|
|
|
342
518
|
|
|
343
519
|
|
|
344
520
|
def install(ctx):
|
|
521
|
+
global g_ctx
|
|
522
|
+
g_ctx = ctx
|
|
345
523
|
# Examples of registering tools using automatic definition generation
|
|
346
524
|
ctx.register_tool(memory_read)
|
|
347
525
|
ctx.register_tool(memory_write)
|
|
@@ -352,7 +530,69 @@ def install(ctx):
|
|
|
352
530
|
ctx.register_tool(glob_paths)
|
|
353
531
|
ctx.register_tool(calc)
|
|
354
532
|
ctx.register_tool(run_python)
|
|
533
|
+
ctx.register_tool(run_typescript)
|
|
534
|
+
ctx.register_tool(run_javascript)
|
|
535
|
+
ctx.register_tool(run_csharp)
|
|
355
536
|
ctx.register_tool(get_current_time)
|
|
356
537
|
|
|
538
|
+
def exec_language(language: str, code: str) -> Dict[str, Any]:
|
|
539
|
+
if language == "python":
|
|
540
|
+
return run_python(code)
|
|
541
|
+
elif language == "typescript":
|
|
542
|
+
return run_typescript(code)
|
|
543
|
+
elif language == "javascript":
|
|
544
|
+
return run_javascript(code)
|
|
545
|
+
elif language == "csharp":
|
|
546
|
+
return run_csharp(code)
|
|
547
|
+
else:
|
|
548
|
+
return {"stdout": "", "stderr": "Error: Invalid language", "returncode": -1}
|
|
549
|
+
|
|
550
|
+
async def run_code(request):
|
|
551
|
+
language = request.match_info["language"]
|
|
552
|
+
code = await request.text()
|
|
553
|
+
try:
|
|
554
|
+
result = exec_language(language, code)
|
|
555
|
+
except Exception as e:
|
|
556
|
+
result = {"stdout": "", "stderr": str(e), "returncode": -1}
|
|
557
|
+
return web.json_response(result)
|
|
558
|
+
|
|
559
|
+
ctx.add_post("code/{language}/run", run_code)
|
|
560
|
+
|
|
561
|
+
async def get_calculator_features(request):
|
|
562
|
+
operators = ["+", "-", "*", "/", "%", "^", "==", "!=", "<", "<=", ">", ">=", "and", "or", "not"]
|
|
563
|
+
operators = [f" {op} " for op in operators]
|
|
564
|
+
constants = ["pi", "e", "inf", "tau", "nan"]
|
|
565
|
+
functions = [f for f in get_calculator_functions() if f not in constants]
|
|
566
|
+
return web.json_response(
|
|
567
|
+
{
|
|
568
|
+
"numbers": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
|
569
|
+
"constants": constants,
|
|
570
|
+
"operators": operators,
|
|
571
|
+
"functions": sorted(functions),
|
|
572
|
+
}
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
ctx.add_get("calc", get_calculator_features)
|
|
576
|
+
|
|
577
|
+
async def run_calc(request):
|
|
578
|
+
code = await request.text()
|
|
579
|
+
result = calc(code)
|
|
580
|
+
return web.json_response({"result": result})
|
|
581
|
+
|
|
582
|
+
ctx.add_post("calc", run_calc)
|
|
583
|
+
|
|
584
|
+
ctx.add_index_footer(
|
|
585
|
+
f"""
|
|
586
|
+
<link rel="stylesheet" href="{ctx.ext_prefix}/codemirror/lib/codemirror.css">
|
|
587
|
+
<link rel="stylesheet" href="{ctx.ext_prefix}/codemirror/theme/mocha.css">
|
|
588
|
+
<script src="{ctx.ext_prefix}/codemirror/lib/codemirror.js"></script>
|
|
589
|
+
<script src="{ctx.ext_prefix}/codemirror/mode/clike/clike.js"></script>
|
|
590
|
+
<script src="{ctx.ext_prefix}/codemirror/mode/javascript/javascript.js"></script>
|
|
591
|
+
<script src="{ctx.ext_prefix}/codemirror/mode/python/python.js"></script>
|
|
592
|
+
<script src="{ctx.ext_prefix}/codemirror/addon/edit/matchbrackets.js"></script>
|
|
593
|
+
<script src="{ctx.ext_prefix}/codemirror/addon/selection/active-line.js"></script>
|
|
594
|
+
"""
|
|
595
|
+
)
|
|
596
|
+
|
|
357
597
|
|
|
358
598
|
__install__ = install
|
|
Binary file
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2
|
+
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
3
|
+
|
|
4
|
+
(function(mod) {
|
|
5
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
6
|
+
mod(require("../../lib/codemirror"));
|
|
7
|
+
else if (typeof define == "function" && define.amd) // AMD
|
|
8
|
+
define(["../../lib/codemirror"], mod);
|
|
9
|
+
else // Plain browser env
|
|
10
|
+
mod(CodeMirror);
|
|
11
|
+
})(function(CodeMirror) {
|
|
12
|
+
var defaults = {
|
|
13
|
+
pairs: "()[]{}''\"\"",
|
|
14
|
+
closeBefore: ")]}'\":;>",
|
|
15
|
+
triples: "",
|
|
16
|
+
explode: "[]{}"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
var Pos = CodeMirror.Pos;
|
|
20
|
+
|
|
21
|
+
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
|
22
|
+
if (old && old != CodeMirror.Init) {
|
|
23
|
+
cm.removeKeyMap(keyMap);
|
|
24
|
+
cm.state.closeBrackets = null;
|
|
25
|
+
}
|
|
26
|
+
if (val) {
|
|
27
|
+
ensureBound(getOption(val, "pairs"))
|
|
28
|
+
cm.state.closeBrackets = val;
|
|
29
|
+
cm.addKeyMap(keyMap);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function getOption(conf, name) {
|
|
34
|
+
if (name == "pairs" && typeof conf == "string") return conf;
|
|
35
|
+
if (typeof conf == "object" && conf[name] != null) return conf[name];
|
|
36
|
+
return defaults[name];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
|
40
|
+
function ensureBound(chars) {
|
|
41
|
+
for (var i = 0; i < chars.length; i++) {
|
|
42
|
+
var ch = chars.charAt(i), key = "'" + ch + "'"
|
|
43
|
+
if (!keyMap[key]) keyMap[key] = handler(ch)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
ensureBound(defaults.pairs + "`")
|
|
47
|
+
|
|
48
|
+
function handler(ch) {
|
|
49
|
+
return function(cm) { return handleChar(cm, ch); };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getConfig(cm) {
|
|
53
|
+
var deflt = cm.state.closeBrackets;
|
|
54
|
+
if (!deflt || deflt.override) return deflt;
|
|
55
|
+
var mode = cm.getModeAt(cm.getCursor());
|
|
56
|
+
return mode.closeBrackets || deflt;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleBackspace(cm) {
|
|
60
|
+
var conf = getConfig(cm);
|
|
61
|
+
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
|
62
|
+
|
|
63
|
+
var pairs = getOption(conf, "pairs");
|
|
64
|
+
var ranges = cm.listSelections();
|
|
65
|
+
for (var i = 0; i < ranges.length; i++) {
|
|
66
|
+
if (!ranges[i].empty()) return CodeMirror.Pass;
|
|
67
|
+
var around = charsAround(cm, ranges[i].head);
|
|
68
|
+
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
|
69
|
+
}
|
|
70
|
+
for (var i = ranges.length - 1; i >= 0; i--) {
|
|
71
|
+
var cur = ranges[i].head;
|
|
72
|
+
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function handleEnter(cm) {
|
|
77
|
+
var conf = getConfig(cm);
|
|
78
|
+
var explode = conf && getOption(conf, "explode");
|
|
79
|
+
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
|
|
80
|
+
|
|
81
|
+
var ranges = cm.listSelections();
|
|
82
|
+
for (var i = 0; i < ranges.length; i++) {
|
|
83
|
+
if (!ranges[i].empty()) return CodeMirror.Pass;
|
|
84
|
+
var around = charsAround(cm, ranges[i].head);
|
|
85
|
+
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
|
86
|
+
}
|
|
87
|
+
cm.operation(function() {
|
|
88
|
+
var linesep = cm.lineSeparator() || "\n";
|
|
89
|
+
cm.replaceSelection(linesep + linesep, null);
|
|
90
|
+
moveSel(cm, -1)
|
|
91
|
+
ranges = cm.listSelections();
|
|
92
|
+
for (var i = 0; i < ranges.length; i++) {
|
|
93
|
+
var line = ranges[i].head.line;
|
|
94
|
+
cm.indentLine(line, null, true);
|
|
95
|
+
cm.indentLine(line + 1, null, true);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function moveSel(cm, dir) {
|
|
101
|
+
var newRanges = [], ranges = cm.listSelections(), primary = 0
|
|
102
|
+
for (var i = 0; i < ranges.length; i++) {
|
|
103
|
+
var range = ranges[i]
|
|
104
|
+
if (range.head == cm.getCursor()) primary = i
|
|
105
|
+
var pos = range.head.ch || dir > 0 ? {line: range.head.line, ch: range.head.ch + dir} : {line: range.head.line - 1}
|
|
106
|
+
newRanges.push({anchor: pos, head: pos})
|
|
107
|
+
}
|
|
108
|
+
cm.setSelections(newRanges, primary)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function contractSelection(sel) {
|
|
112
|
+
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
|
|
113
|
+
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
|
|
114
|
+
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function handleChar(cm, ch) {
|
|
118
|
+
var conf = getConfig(cm);
|
|
119
|
+
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
|
120
|
+
|
|
121
|
+
var pairs = getOption(conf, "pairs");
|
|
122
|
+
var pos = pairs.indexOf(ch);
|
|
123
|
+
if (pos == -1) return CodeMirror.Pass;
|
|
124
|
+
|
|
125
|
+
var closeBefore = getOption(conf,"closeBefore");
|
|
126
|
+
|
|
127
|
+
var triples = getOption(conf, "triples");
|
|
128
|
+
|
|
129
|
+
var identical = pairs.charAt(pos + 1) == ch;
|
|
130
|
+
var ranges = cm.listSelections();
|
|
131
|
+
var opening = pos % 2 == 0;
|
|
132
|
+
|
|
133
|
+
var type;
|
|
134
|
+
for (var i = 0; i < ranges.length; i++) {
|
|
135
|
+
var range = ranges[i], cur = range.head, curType;
|
|
136
|
+
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
|
137
|
+
if (opening && !range.empty()) {
|
|
138
|
+
curType = "surround";
|
|
139
|
+
} else if ((identical || !opening) && next == ch) {
|
|
140
|
+
if (identical && stringStartsAfter(cm, cur))
|
|
141
|
+
curType = "both";
|
|
142
|
+
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
|
|
143
|
+
curType = "skipThree";
|
|
144
|
+
else
|
|
145
|
+
curType = "skip";
|
|
146
|
+
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
|
|
147
|
+
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
|
|
148
|
+
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
|
|
149
|
+
curType = "addFour";
|
|
150
|
+
} else if (identical) {
|
|
151
|
+
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
|
|
152
|
+
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
|
|
153
|
+
else return CodeMirror.Pass;
|
|
154
|
+
} else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
|
|
155
|
+
curType = "both";
|
|
156
|
+
} else {
|
|
157
|
+
return CodeMirror.Pass;
|
|
158
|
+
}
|
|
159
|
+
if (!type) type = curType;
|
|
160
|
+
else if (type != curType) return CodeMirror.Pass;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
|
|
164
|
+
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
|
|
165
|
+
cm.operation(function() {
|
|
166
|
+
if (type == "skip") {
|
|
167
|
+
moveSel(cm, 1)
|
|
168
|
+
} else if (type == "skipThree") {
|
|
169
|
+
moveSel(cm, 3)
|
|
170
|
+
} else if (type == "surround") {
|
|
171
|
+
var sels = cm.getSelections();
|
|
172
|
+
for (var i = 0; i < sels.length; i++)
|
|
173
|
+
sels[i] = left + sels[i] + right;
|
|
174
|
+
cm.replaceSelections(sels, "around");
|
|
175
|
+
sels = cm.listSelections().slice();
|
|
176
|
+
for (var i = 0; i < sels.length; i++)
|
|
177
|
+
sels[i] = contractSelection(sels[i]);
|
|
178
|
+
cm.setSelections(sels);
|
|
179
|
+
} else if (type == "both") {
|
|
180
|
+
cm.replaceSelection(left + right, null);
|
|
181
|
+
cm.triggerElectric(left + right);
|
|
182
|
+
moveSel(cm, -1)
|
|
183
|
+
} else if (type == "addFour") {
|
|
184
|
+
cm.replaceSelection(left + left + left + left, "before");
|
|
185
|
+
moveSel(cm, 1)
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function charsAround(cm, pos) {
|
|
191
|
+
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
|
|
192
|
+
Pos(pos.line, pos.ch + 1));
|
|
193
|
+
return str.length == 2 ? str : null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function stringStartsAfter(cm, pos) {
|
|
197
|
+
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
|
|
198
|
+
return /\bstring/.test(token.type) && token.start == pos.ch &&
|
|
199
|
+
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
|
|
200
|
+
}
|
|
201
|
+
});
|