Skip to content

Commit 7a48aa9

Browse files
fix xlam's schema issues (#1917)
fix xlam Signed-off-by: dafnapension <dafnashein@yahoo.com> Co-authored-by: Elron Bandel <elronbandel@gmail.com>
1 parent 4bf8afa commit 7a48aa9

3 files changed

Lines changed: 119 additions & 14 deletions

File tree

prepare/cards/xlam_function_calling.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from unitxt.operators import (
55
Copy,
66
ExecuteExpression,
7+
FixJsonSchemaOfParameterTypes,
78
Move,
89
Set,
910
)
@@ -24,22 +25,22 @@
2425
LoadJson(field="answers", to_field="reference_calls"),
2526
LoadJson(field="tools"),
2627
Move(field="tools/*/parameters", to_field="properties"),
27-
Set(fields={"tools/*/parameters": {"type": "object"}}, use_deepcopy=True),
2828
Copy(
2929
field="properties",
3030
to_field="tools/*/parameters/properties",
3131
set_every_value=True,
3232
),
33+
Set(fields={"tools/*/parameters/type": "object"}, use_deepcopy=True),
3334
ExecuteExpression(
3435
to_field="required",
35-
expression="[[p for p, c in tool['parameters']['properties'].items() if 'optional' not in c['type']] for tool in tools]",
36+
expression="[[p for p, c in tool['parameters']['properties'].items() if 'optional' not in c['type'].lower()] for tool in tools]",
3637
),
3738
Copy(
3839
field="required",
3940
to_field="tools/*/parameters/required",
4041
set_every_value=True,
4142
),
42-
"operators.fix_json_schema",
43+
FixJsonSchemaOfParameterTypes(main_field="tools"),
4344
],
4445
task="tasks.tool_calling.multi_turn",
4546
templates=["templates.tool_calling.multi_turn"],

src/unitxt/catalog/cards/xlam_function_calling_60k.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,34 @@
4545
"field": "tools/*/parameters",
4646
"to_field": "properties"
4747
},
48-
{
49-
"__type__": "set",
50-
"fields": {
51-
"tools/*/parameters": {
52-
"type": "object"
53-
}
54-
},
55-
"use_deepcopy": true
56-
},
5748
{
5849
"__type__": "copy",
5950
"field": "properties",
6051
"to_field": "tools/*/parameters/properties",
6152
"set_every_value": true
6253
},
54+
{
55+
"__type__": "set",
56+
"fields": {
57+
"tools/*/parameters/type": "object"
58+
},
59+
"use_deepcopy": true
60+
},
6361
{
6462
"__type__": "execute_expression",
6563
"to_field": "required",
66-
"expression": "[[p for p, c in tool['parameters']['properties'].items() if 'optional' not in c['type']] for tool in tools]"
64+
"expression": "[[p for p, c in tool['parameters']['properties'].items() if 'optional' not in c['type'].lower()] for tool in tools]"
6765
},
6866
{
6967
"__type__": "copy",
7068
"field": "required",
7169
"to_field": "tools/*/parameters/required",
7270
"set_every_value": true
7371
},
74-
"operators.fix_json_schema"
72+
{
73+
"__type__": "fix_json_schema_of_parameter_types",
74+
"main_field": "tools"
75+
}
7576
],
7677
"task": "tasks.tool_calling.multi_turn",
7778
"templates": [

src/unitxt/operators.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"""
4141

4242
import operator
43+
import re
4344
import uuid
4445
import warnings
4546
import zipfile
@@ -2611,3 +2612,105 @@ def process_value(self, value: str) -> str:
26112612
# Read from local file
26122613
with open(value, encoding=self.encoding) as f:
26132614
return f.read()
2615+
2616+
2617+
class FixJsonSchemaOfParameterTypes(InstanceOperator):
2618+
main_field: str
2619+
2620+
def prepare(self):
2621+
self.simple_mapping = {
2622+
"": "object",
2623+
"any": "object",
2624+
"Any": "object",
2625+
"Array": "array",
2626+
"ArrayList": "array",
2627+
"Bigint": "integer",
2628+
"bool": "boolean",
2629+
"Boolean": "boolean",
2630+
"byte": "integer",
2631+
"char": "string",
2632+
"dict": "object",
2633+
"Dict": "object",
2634+
"double": "number",
2635+
"float": "number",
2636+
"HashMap": "object",
2637+
"Hashtable": "object",
2638+
"int": "integer",
2639+
"list": "array",
2640+
"List": "array",
2641+
"long": "integer",
2642+
"Queue": "array",
2643+
"short": "integer",
2644+
"Stack": "array",
2645+
"tuple": "array",
2646+
"Set": "array",
2647+
"set": "array",
2648+
"str": "string",
2649+
"String": "string",
2650+
}
2651+
2652+
def dict_type_of(self, type_str: str) -> dict:
2653+
return {"type": type_str}
2654+
2655+
def recursive_trace_for_type_fields(self, containing_element):
2656+
if isinstance(containing_element, dict):
2657+
keys = list(containing_element.keys())
2658+
for key in keys:
2659+
if key == "type" and isinstance(containing_element["type"], str):
2660+
jsonschema_dict = self.type_str_to_jsonschema_dict(
2661+
containing_element["type"]
2662+
)
2663+
containing_element.pop("type")
2664+
containing_element.update(jsonschema_dict)
2665+
else:
2666+
self.recursive_trace_for_type_fields(containing_element[key])
2667+
elif isinstance(containing_element, list):
2668+
for list_element in containing_element:
2669+
self.recursive_trace_for_type_fields(list_element)
2670+
2671+
def type_str_to_jsonschema_dict(self, type_str: str) -> dict:
2672+
if type_str in self.simple_mapping:
2673+
return self.dict_type_of(self.simple_mapping[type_str])
2674+
m = re.match(r"^(List|Tuple)\[(.*?)\]$", type_str)
2675+
if m:
2676+
basic_type = self.dict_type_of("array")
2677+
basic_type["items"] = self.type_str_to_jsonschema_dict(
2678+
m.group(2) if m.group(1) == "List" else m.group(2).split(",")[0].strip()
2679+
)
2680+
return basic_type
2681+
2682+
m = re.match(r"^(Union)\[(.*?)\]$", type_str)
2683+
if m:
2684+
args = m.group(2).split(",")
2685+
for i in range(len(args)):
2686+
args[i] = args[i].strip()
2687+
return {"anyOf": [self.type_str_to_jsonschema_dict(arg) for arg in args]}
2688+
if re.match(r"^(Callable)\[(.*?)\]$", type_str):
2689+
return self.dict_type_of("object")
2690+
if "," in type_str:
2691+
sub_types = type_str.split(",")
2692+
for i in range(len(sub_types)):
2693+
sub_types[i] = sub_types[i].strip()
2694+
assert len(sub_types) in [
2695+
2,
2696+
3,
2697+
], f"num of subtypes should be 2 or 3, got {type_str}"
2698+
basic_type = self.type_str_to_jsonschema_dict(sub_types[0])
2699+
for sub_type in sub_types[1:]:
2700+
if sub_type.lower().startswith("default"):
2701+
basic_type["default"] = re.split(r"[= ]", sub_type, maxsplit=1)[1]
2702+
for sub_type in sub_types[1:]:
2703+
if sub_type.lower().startswith("optional"):
2704+
return {"anyOf": [basic_type, self.dict_type_of("null")]}
2705+
return basic_type
2706+
2707+
return self.dict_type_of(type_str) # otherwise - return what arrived
2708+
2709+
def process(
2710+
self, instance: Dict[str, Any], stream_name: Optional[str] = None
2711+
) -> Dict[str, Any]:
2712+
assert (
2713+
self.main_field in instance
2714+
), f"field '{self.main_field}' must reside in instance in order to verify its jsonschema correctness. got {instance}"
2715+
self.recursive_trace_for_type_fields(instance[self.main_field])
2716+
return instance

0 commit comments

Comments
 (0)