llama.cpp/examples/openai/ts_converter.py

98 lines
3.9 KiB
Python

from typing import Any, List, Set, Tuple, Union
import json
class SchemaToTypeScriptConverter:
# TODO: comments for arguments!
# // Get the price of a particular car model
# type get_car_price = (_: {
# // The name of the car model.
# car_name: string,
# }) => any;
# // get the weather of a location
# type get_weather = (_: {
# // where to get weather.
# location: string,
# }) => any;
def _desc_comment(self, schema: dict):
desc = schema.get("description", "").replace("\n", "\n// ") if 'description' in schema else None
return f'// {desc}\n' if desc else ''
def _build_object_rule(self, properties: List[Tuple[str, Any]], required: Set[str], additional_properties: Union[bool, Any]):
if additional_properties == True:
additional_properties = {}
elif additional_properties == False:
additional_properties = None
return "{" + ', '.join([
f'{self._desc_comment(prop_schema)}{prop_name}{"" if prop_name in required else "?"}: {self.visit(prop_schema)}'
for prop_name, prop_schema in properties
] + (
[f"{self._desc_comment(additional_properties) if additional_properties else ''}[key: string]: {self.visit(additional_properties)}"]
if additional_properties is not None else []
)) + "}"
def visit(self, schema: dict):
def print_constant(v):
return json.dumps(v)
schema_type = schema.get('type')
schema_format = schema.get('format')
if 'oneOf' in schema or 'anyOf' in schema:
return '|'.join(self.visit(s) for s in schema.get('oneOf') or schema.get('anyOf'))
elif isinstance(schema_type, list):
return '|'.join(self.visit({'type': t}) for t in schema_type)
elif 'const' in schema:
return print_constant(schema['const'])
elif 'enum' in schema:
return '|'.join((print_constant(v) for v in schema['enum']))
elif schema_type in (None, 'object') and \
('properties' in schema or \
('additionalProperties' in schema and schema['additionalProperties'] is not True)):
required = set(schema.get('required', []))
properties = list(schema.get('properties', {}).items())
return self._build_object_rule(properties, required, schema.get('additionalProperties'))
elif schema_type in (None, 'object') and 'allOf' in schema:
required = set()
properties = []
def add_component(comp_schema, is_required):
if (ref := comp_schema.get('$ref')) is not None:
comp_schema = self._refs[ref]
if 'properties' in comp_schema:
for prop_name, prop_schema in comp_schema['properties'].items():
properties.append((prop_name, prop_schema))
if is_required:
required.add(prop_name)
for t in schema['allOf']:
if 'anyOf' in t:
for tt in t['anyOf']:
add_component(tt, is_required=False)
else:
add_component(t, is_required=True)
return self._build_object_rule(properties, required, additional_properties=[])
elif schema_type in (None, 'array') and ('items' in schema or 'prefixItems' in schema):
items = schema.get('items') or schema['prefixItems']
if isinstance(items, list):
return '[' + ', '.join(self.visit(item) for item in items) + '][]'
else:
return self.visit(items) + '[]'
elif schema_type in (None, 'string') and schema_format == 'date-time':
return 'Date'
elif (schema_type == 'object') or (len(schema) == 0):
return 'any'
else:
return 'number' if schema_type == 'integer' else schema_type