33import pyodideModule from "npm:pyodide@0.29.4/pyodide.js" ;
44import { readLines } from "https://deno.land/std@0.186.0/io/mod.ts" ;
55
6+ const JSON = Object . freeze ( globalThis . JSON ) , console = Object . freeze ( globalThis . console ) ;
67// =============================================================================
78// Python Code Templates
89// =============================================================================
@@ -19,32 +20,32 @@ sys.stdout, sys.stderr = buf_stdout, buf_stderr
1920def last_exception_args():
2021 return json.dumps(sys.last_exc.args) if sys.last_exc else None
2122
22- class FinalOutput (BaseException):
23+ class _DSPyFinalOutput (BaseException):
2324 # Control-flow exception to signal completion (like StopIteration)
2425 pass
2526
2627# Default SUBMIT for single-output signatures (e.g., Program of Thought).
2728# Only define if not already registered with typed signatures.
2829if 'SUBMIT' not in dir():
2930 def SUBMIT(output):
30- raise FinalOutput ({"output": output})
31+ raise _DSPyFinalOutput ({"output": output})
3132` ;
3233
3334// Generate a tool wrapper function with typed signature.
3435// Parameters is an array of {name, type?, default?} objects.
3536// Convert a JavaScript/JSON value to Python literal syntax
3637const toPythonLiteral = ( value ) => {
37- if ( value === null ) return 'None' ;
38- if ( value === true ) return 'True' ;
39- if ( value === false ) return 'False' ;
40- return JSON . stringify ( value ) ; // Works for strings, numbers, arrays, objects
38+ const serialized = JSON . stringify ( value ) ;
39+ return `__import__("json").loads(${ JSON . stringify ( serialized ) } )` ;
4140} ;
4241
42+ const toPythonType = ( type ) => type === "NoneType" ? "type(None)" : type ;
43+
4344const makeToolWrapper = ( toolName , parameters = [ ] ) => {
4445 // Build signature parts: "query: str, limit: int = 10"
4546 const sigParts = parameters . map ( p => {
4647 let part = p . name ;
47- if ( p . type ) part += `: ${ p . type } ` ;
48+ if ( p . type ) part += `: ${ toPythonType ( p . type ) } ` ;
4849 if ( p . default !== undefined ) part += ` = ${ toPythonLiteral ( p . default ) } ` ;
4950 return part ;
5051 } ) ;
@@ -71,20 +72,20 @@ const makeSubmitWrapper = (outputs) => {
7172 // Fallback to single-arg SUBMIT if no outputs defined
7273 return `
7374def SUBMIT(output):
74- raise FinalOutput ({"output": output})
75+ raise _DSPyFinalOutput ({"output": output})
7576` ;
7677 }
7778
7879 const sigParts = outputs . map ( o => {
7980 let part = o . name ;
80- if ( o . type ) part += `: ${ o . type } ` ;
81+ if ( o . type ) part += `: ${ toPythonType ( o . type ) } ` ;
8182 return part ;
8283 } ) ;
8384 const dictParts = outputs . map ( o => `"${ o . name } ": ${ o . name } ` ) ;
8485
8586 return `
8687def SUBMIT(${ sigParts . join ( ', ' ) } ):
87- raise FinalOutput ({${ dictParts . join ( ', ' ) } })
88+ raise _DSPyFinalOutput ({${ dictParts . join ( ', ' ) } })
8889` ;
8990} ;
9091
@@ -114,7 +115,7 @@ const JSONRPC_APP_ERRORS = {
114115} ;
115116
116117const jsonrpcRequest = ( method , params , id ) =>
117- JSON . stringify ( { jsonrpc : "2.0" , method, params, id } ) ;
118+ JSON . stringify ( { __proto__ : null , jsonrpc : "2.0" , method, params : { __proto__ : null , ... params } , id } ) ;
118119
119120const jsonrpcNotification = ( method , params = null ) => {
120121 const msg = { jsonrpc : "2.0" , method } ;
@@ -351,8 +352,8 @@ while (true) {
351352 // error.message is mostly blank.
352353 const errorMessage = ( error . message || "" ) . trim ( ) ;
353354
354- // Handle FinalOutput as a success result, not an error
355- if ( errorType === "FinalOutput " ) {
355+ // Handle SUBMIT's control-flow exception as a success result, not an error
356+ if ( errorType === "_DSPyFinalOutput " ) {
356357 const last_exception_args = pyodide . globals . get ( "last_exception_args" ) ;
357358 const errorArgs = JSON . parse ( last_exception_args ( ) ) || [ ] ;
358359 const answer = errorArgs [ 0 ] || null ;
0 commit comments