Skip to content

Commit 3038219

Browse files
authored
feature: add _resolve_schema_refs (#57)
* feature: add _resolve_schema_refs * feature: add anyOf/oneOf in json schema in _resolve_schema_refs
1 parent 3c87ab0 commit 3038219

File tree

1 file changed

+65
-1
lines changed

1 file changed

+65
-1
lines changed

gpt2giga/protocol/request_mapper.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,66 @@
1616
class RequestTransformer:
1717
"""Трансформер запросов из OpenAI в GigaChat формат"""
1818

19+
@staticmethod
20+
def _resolve_schema_refs(schema: Dict[str, Any]) -> Dict[str, Any]:
21+
"""
22+
Разрешает $ref ссылки и anyOf/oneOf в JSON schema.
23+
GigaChat не поддерживает $ref/$defs и anyOf/oneOf, поэтому нужно
24+
развернуть схему и упростить Optional типы.
25+
"""
26+
27+
def resolve(obj: Any, defs: Dict[str, Any]) -> Any:
28+
if isinstance(obj, dict):
29+
# Handle $ref
30+
if "$ref" in obj:
31+
ref_path = obj["$ref"]
32+
# Parse reference like '#/$defs/Step'
33+
if ref_path.startswith("#/$defs/"):
34+
ref_name = ref_path.split("/")[-1]
35+
if ref_name in defs:
36+
# Return resolved definition (recursively resolve)
37+
resolved = defs[ref_name].copy()
38+
return resolve(resolved, defs)
39+
return obj
40+
41+
# Handle anyOf/oneOf (typically from Optional types)
42+
# Pydantic generates: anyOf: [{actual_type}, {type: "null"}]
43+
for union_key in ("anyOf", "oneOf"):
44+
if union_key in obj:
45+
variants = obj[union_key]
46+
# Find non-null variant
47+
non_null_variants = [
48+
v for v in variants if v.get("type") != "null"
49+
]
50+
if non_null_variants:
51+
# Take the first non-null variant and merge with other props
52+
result = resolve(non_null_variants[0], defs)
53+
# Preserve other properties like 'default', 'title', 'description'
54+
for key, value in obj.items():
55+
if (
56+
key not in (union_key, "$defs")
57+
and key not in result
58+
):
59+
result[key] = resolve(value, defs)
60+
return result
61+
# If all are null, just return null type
62+
return {"type": "null"}
63+
64+
# Recursively process dict, skipping $defs
65+
return {
66+
key: resolve(value, defs)
67+
for key, value in obj.items()
68+
if key != "$defs"
69+
}
70+
71+
elif isinstance(obj, list):
72+
return [resolve(item, defs) for item in obj]
73+
74+
return obj
75+
76+
defs = schema.get("$defs", {})
77+
return resolve(schema, defs)
78+
1979
@staticmethod
2080
def _ensure_json_object_str(value: Any) -> str:
2181
"""
@@ -247,10 +307,13 @@ def _apply_json_schema_as_function(
247307
transformed: Dict, schema_name: str, schema: Dict
248308
) -> None:
249309
"""Применяет JSON schema как function call для structured output"""
310+
# Разрешаем $ref/$defs ссылки, т.к. GigaChat их не поддерживает
311+
resolved_schema = RequestTransformer._resolve_schema_refs(schema)
312+
250313
function_def = {
251314
"name": schema_name,
252315
"description": f"Output response in structured format: {schema_name}",
253-
"parameters": schema,
316+
"parameters": resolved_schema,
254317
}
255318

256319
if "functions" not in transformed:
@@ -271,6 +334,7 @@ def transform_chat_parameters(self, data: Dict) -> Dict:
271334
schema = json_schema.get("schema")
272335
self._apply_json_schema_as_function(transformed, schema_name, schema)
273336
else:
337+
print(response_format)
274338
transformed["response_format"] = {
275339
"type": response_format.get("type"),
276340
**response_format.get("json_schema", {}),

0 commit comments

Comments
 (0)