import sys
import os
from typing import Dict, Any, Set
from utils import load_spec, save_spec
def find_model_fields_in_allof(spec: Dict[str, Any]) -> Set[str]:
schemas = spec.get('components', {}).get('schemas', {})
model_schemas = set()
for schema_name, schema in schemas.items():
if 'allOf' in schema:
for ref_item in schema.get('allOf', []):
if '$ref' in ref_item:
ref_path = ref_item['$ref'].split('/')[-1]
ref_schema = schemas.get(ref_path, {})
if 'properties' in ref_schema and 'model' in ref_schema.get('properties', {}):
model_schemas.add(schema_name)
print(f"Found {schema_name} inherits model from {ref_path}")
return model_schemas
def flatten_model_fields(spec: Dict[str, Any]) -> Dict[str, Any]:
schemas = spec.get('components', {}).get('schemas', {})
changes = []
model_id_schemas = [
'ModelIds',
'ModelIdsShared',
'ModelIdsResponses',
'CreateCompletionRequestModel',
'CreateAssistantRequestModel',
'CreateThreadAndRunRequestModel',
'CreateImageRequestModel',
'CreateEmbeddingRequestModel',
'CreateSpeechRequestModel',
'CreateFineTuningJobRequestModel',
'CreateTranscriptionRequestModel',
'CreateTranslationRequestModel',
'CreateModerationRequestModel',
'CreateImageEditRequestModel',
'CreateImageVariationRequestModel',
'VoiceIdsShared'
]
for schema_name in model_id_schemas:
if schema_name in schemas:
old_schema = schemas[schema_name]
schemas[schema_name] = {
'type': 'string',
'description': old_schema.get('description', f'Model identifier as string')
}
changes.append(f"Replaced {schema_name} with simple string type")
for schema_name, schema in schemas.items():
if 'allOf' in schema:
has_model = False
for ref_item in schema.get('allOf', []):
if '$ref' in ref_item:
ref_name = ref_item['$ref'].split('/')[-1]
ref_schema = schemas.get(ref_name, {})
if 'properties' in ref_schema and 'model' in ref_schema.get('properties', {}):
has_model = True
break
if has_model:
for item in schema['allOf']:
if 'type' in item and item['type'] == 'object':
if 'properties' not in item:
item['properties'] = {}
item['properties']['model'] = {
'type': 'string',
'description': 'ID of the model to use'
}
changes.append(f"Injected model field into {schema_name}")
break
for schema_name, schema in schemas.items():
if 'properties' in schema:
props = schema['properties']
if 'model' in props:
model_prop = props['model']
if '$ref' in model_prop:
ref_name = model_prop['$ref'].split('/')[-1]
if ref_name in model_id_schemas:
props['model'] = {
'type': 'string',
'description': model_prop.get('description', 'ID of the model to use')
}
changes.append(f"Converted {schema_name}.model from $ref to string")
elif 'allOf' in model_prop or 'oneOf' in model_prop or 'anyOf' in model_prop:
props['model'] = {
'type': 'string',
'description': model_prop.get('description', 'ID of the model to use')
}
changes.append(f"Simplified {schema_name}.model from union to string")
for schema_name, schema in schemas.items():
if 'properties' in schema:
props = schema['properties']
if 'voice' in props:
voice_prop = props['voice']
if '$ref' in voice_prop:
ref_name = voice_prop['$ref'].split('/')[-1]
if ref_name == 'VoiceIdsShared':
props['voice'] = {
'type': 'string',
'description': voice_prop.get('description', 'The voice to use for audio generation')
}
changes.append(f"Converted {schema_name}.voice from $ref to string")
elif 'allOf' in voice_prop or 'oneOf' in voice_prop or 'anyOf' in voice_prop:
props['voice'] = {
'type': 'string',
'description': voice_prop.get('description', 'The voice to use for audio generation')
}
changes.append(f"Simplified {schema_name}.voice from union to string")
if 'ModelResponseProperties' in schemas:
model_props = schemas['ModelResponseProperties']
if 'properties' in model_props and 'model' in model_props['properties']:
model_props['properties']['model'] = {
'type': 'string',
'description': 'ID of the model to use'
}
changes.append("Fixed ModelResponseProperties.model to be string")
return changes
def main():
input_path = sys.argv[1] if len(sys.argv) > 1 else 'stainless.yaml'
output_path = sys.argv[2] if len(sys.argv) > 2 else 'target/specs/l1_model_fixed.yaml'
print(f"Loading spec from {input_path}")
spec = load_spec(input_path)
print("\nFinding model fields in allOf inheritance...")
model_schemas = find_model_fields_in_allof(spec)
print(f"Found {len(model_schemas)} schemas with inherited model fields")
print("\nFlattening model field definitions...")
changes = flatten_model_fields(spec)
print("\nChanges made:")
for change in changes:
print(f" - {change}")
print(f"\nSaving fixed spec to {output_path}")
save_spec(spec, output_path)
report_path = 'target/reports/l1_model_fields_fix_report.md'
with open(report_path, 'w') as f:
f.write("# Model Fields Fix Report\n\n")
f.write(f"- Input: {os.path.relpath(input_path)}\n")
f.write(f"- Output: {os.path.relpath(output_path)}\n")
f.write(f"- Total changes: {len(changes)}\n\n")
f.write("## Changes Applied\n\n")
for change in changes:
f.write(f"- {change}\n")
print(f"Report saved to {report_path}")
print(f"\nTotal changes: {len(changes)}")
if __name__ == "__main__":
main()