99 full_write_guard ,
1010)
1111
12- from agenta .sdk .engines .running .runners .base import CodeRunner
12+ from agenta .sdk .engines .running .runners .base import CodeRunner , normalize_result
1313
1414
1515# Pure data/iteration builtins that RestrictedPython's safe_builtins omits but
1616# evaluators routinely need. All operate on data only — none reach the host or
17- # the class graph, so adding them does not widen the sandbox (escapes go through
18- # attribute access, which safer_getattr blocks).
17+ # the class graph.
1918_SAFE_EXTRA_BUILTINS = (
2019 "dict" ,
2120 "list" ,
3837# pathlib, socket, importlib, io, shutil, ...) or the network (httpx, urllib,
3938# requests, ...) is excluded. Operators who need unrestricted execution must opt
4039# into the `local` runner; hostile multi-tenant should use `daytona`.
40+ #
41+ # EXCLUDED intentionally even though they look safe:
42+ # - `typing` — exposes `typing.sys`, giving access to `sys.modules` and
43+ # therefore every already-loaded module (including `os`).
44+ # - `datetime` — same escape: `datetime.sys.modules`.
45+ # - `statistics`— same escape: `statistics.sys.modules`.
46+ # safer_getattr only blocks underscore-prefixed names; plain public attributes
47+ # like `sys` and `modules` on imported module objects are not blocked.
4148_ALLOWED_IMPORTS = frozenset (
4249 {
4350 "math" ,
44- "statistics" ,
45- "datetime" ,
4651 "json" ,
4752 "re" ,
4853 "random" ,
4954 "string" ,
50- "typing" ,
5155 "collections" ,
5256 "itertools" ,
5357 "functools" ,
@@ -129,16 +133,21 @@ def run(
129133 trace: Full trace data (v2 only)
130134
131135 Returns:
132- Float score between 0 and 1, or None if execution fails
136+ Versions "1"/"2": float score between 0 and 1.
137+ Version "3": any JSON-serializable value (dict, list, str, float, bool).
133138 """
134139 # Normalize runtime: None means python
135140 runtime = runtime or "python"
136141
137142 # The restricted sandbox runs in-process and only supports Python.
143+ # JavaScript and TypeScript require the Daytona runner
144+ # (AGENTA_SERVICES_CODE_SANDBOX_RUNNER=daytona).
138145 if runtime != "python" :
139146 raise ValueError (
140- f"RestrictedRunner only supports 'python' runtime, got: { runtime } . "
141- "Use the Daytona runner for javascript/typescript."
147+ f"Runtime '{ runtime } ' is not supported by the default sandbox. "
148+ "JavaScript and TypeScript evaluators require the Daytona runner. "
149+ "Set AGENTA_SERVICES_CODE_SANDBOX_RUNNER=daytona, or change the "
150+ "runtime to 'python'."
142151 )
143152
144153 try :
@@ -153,24 +162,12 @@ def run(
153162
154163 fn = environment ["evaluate" ]
155164
156- if version == "2" :
165+ if version in ( "2" , "3" ) :
157166 result = fn (inputs , output , trace )
158167 else :
159168 result = fn (app_params , inputs , output , correct_answer )
160169
161- # Attempt to convert result to float
162- if isinstance (result , (float , int , str )):
163- try :
164- result = float (result )
165- except ValueError as e :
166- raise ValueError (f"Result cannot be converted to float: { e } " )
167-
168- if not isinstance (result , float ):
169- raise TypeError (
170- f"Result is not a float after conversion: { type (result )} "
171- )
172-
173- return result
170+ return normalize_result (result , version )
174171
175172 except KeyError as e :
176173 raise KeyError (f"Missing expected key in environment: { e } " )
0 commit comments