1010
1111API_KEY = os .getenv ("OPENAI_API_KEY" )
1212ENV_VAR_PREFIX = "ASK2API_"
13+ TYPE_HINTS = {
14+ "string" : "string" ,
15+ "str" : "string" ,
16+ "number" : "number" ,
17+ "int" : "integer" ,
18+ "integer" : "integer" ,
19+ "float" : "number" ,
20+ "bool" : "boolean" ,
21+ "boolean" : "boolean" ,
22+ "array" : "array" ,
23+ "list" : "array" ,
24+ "object" : "object" ,
25+ "dict" : "object" ,
26+ }
1327
1428
1529@dataclass
@@ -91,6 +105,66 @@ def get_version():
91105 return "dev"
92106
93107
108+ def convert_example_to_schema (example , _cache = None ):
109+ """Convert a JSON example to a JSON Schema with memoization."""
110+ if _cache is None :
111+ _cache = {}
112+
113+ # Use id() for memoization key to handle nested structures
114+ cache_key = id (example )
115+ if cache_key in _cache :
116+ return _cache [cache_key ]
117+
118+ if isinstance (example , dict ):
119+ schema = {
120+ "type" : "object" ,
121+ "properties" : {},
122+ "required" : list (example .keys ()),
123+ "additionalProperties" : False ,
124+ }
125+
126+ for key , value in example .items ():
127+ if isinstance (value , str ):
128+ schema ["properties" ][key ] = {
129+ "type" : TYPE_HINTS .get (value .lower (), "string" )
130+ }
131+ elif isinstance (value , bool ):
132+ schema ["properties" ][key ] = {"type" : "boolean" }
133+ elif isinstance (value , int ):
134+ schema ["properties" ][key ] = {"type" : "integer" }
135+ elif isinstance (value , float ):
136+ schema ["properties" ][key ] = {"type" : "number" }
137+ elif isinstance (value , list ):
138+ schema ["properties" ][key ] = {
139+ "type" : "array" ,
140+ "items" : convert_example_to_schema (value [0 ], _cache )
141+ if value
142+ else {},
143+ }
144+ elif isinstance (value , dict ):
145+ schema ["properties" ][key ] = convert_example_to_schema (value , _cache )
146+ else :
147+ schema ["properties" ][key ] = {"type" : "string" }
148+
149+ _cache [cache_key ] = schema
150+ return schema
151+
152+ elif isinstance (example , list ):
153+ schema = {
154+ "type" : "array" ,
155+ "items" : convert_example_to_schema (example [0 ], _cache ) if example else {},
156+ }
157+ _cache [cache_key ] = schema
158+ return schema
159+
160+ else :
161+ # Primitive types - use type() for faster checking
162+ type_map = {str : "string" , bool : "boolean" , int : "integer" , float : "number" }
163+ schema = {"type" : type_map .get (type (example ), "string" )}
164+ _cache [cache_key ] = schema
165+ return schema
166+
167+
94168def main ():
95169 env_vars_help = """
96170Environment Variables:
@@ -109,10 +183,15 @@ def main():
109183 required = True ,
110184 help = "Natural language prompt" ,
111185 )
112- parser .add_argument (
186+ schema_group = parser .add_mutually_exclusive_group (required = True )
187+ schema_group .add_argument (
188+ "-e" ,
189+ "--example" ,
190+ help = 'JSON example as a string (e.g., \' {"country": "France", "city": "Paris"}\' )' ,
191+ )
192+ schema_group .add_argument (
113193 "-sf" ,
114194 "--schema-file" ,
115- required = True ,
116195 help = "Path to JSON schema file" ,
117196 )
118197 parser .add_argument (
@@ -128,8 +207,13 @@ def main():
128207 )
129208 args = parser .parse_args ()
130209
131- with open (args .schema_file , "r" , encoding = "utf-8" ) as f :
132- schema = json .load (f )
210+ # Load schema from file or parse from string
211+ if args .schema_file :
212+ with open (args .schema_file , "r" , encoding = "utf-8" ) as f :
213+ schema = json .load (f )
214+ else :
215+ example = json .loads (args .example )
216+ schema = convert_example_to_schema (example )
133217
134218 system_prompt = """
135219 You are a JSON API engine.
0 commit comments