synth-ai 0.1.0.dev13__py3-none-any.whl → 0.1.0.dev15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- synth_ai/zyk/lms/structured_outputs/inject.py +155 -26
- synth_ai/zyk/lms/vendors/openai_standard.py +4 -1
- {synth_ai-0.1.0.dev13.dist-info → synth_ai-0.1.0.dev15.dist-info}/METADATA +1 -1
- {synth_ai-0.1.0.dev13.dist-info → synth_ai-0.1.0.dev15.dist-info}/RECORD +7 -7
- {synth_ai-0.1.0.dev13.dist-info → synth_ai-0.1.0.dev15.dist-info}/WHEEL +1 -1
- {synth_ai-0.1.0.dev13.dist-info → synth_ai-0.1.0.dev15.dist-info}/LICENSE +0 -0
- {synth_ai-0.1.0.dev13.dist-info → synth_ai-0.1.0.dev15.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import (
|
|
3
|
+
Any,
|
|
4
|
+
Dict,
|
|
5
|
+
List,
|
|
6
|
+
Optional,
|
|
7
|
+
Tuple,
|
|
8
|
+
Type,
|
|
9
|
+
get_type_hints,
|
|
10
|
+
get_args,
|
|
11
|
+
get_origin,
|
|
12
|
+
Union,
|
|
13
|
+
Literal,
|
|
14
|
+
)
|
|
3
15
|
from pydantic import BaseModel
|
|
4
16
|
import warnings
|
|
5
17
|
|
|
@@ -29,13 +41,16 @@ def generate_type_map() -> Dict[Any, str]:
|
|
|
29
41
|
# Handle generic Dict type
|
|
30
42
|
type_map[Dict] = "Dict[Any,Any]"
|
|
31
43
|
# Provide both key and value types for Dict
|
|
32
|
-
type_map[Dict[base_type, base_type]] =
|
|
44
|
+
type_map[Dict[base_type, base_type]] = (
|
|
45
|
+
f"{collection_name}[{name},{name}]"
|
|
46
|
+
)
|
|
33
47
|
# Handle Dict[Any, Any] explicitly
|
|
34
48
|
type_map[Dict[Any, Any]] = "Dict[Any,Any]"
|
|
35
49
|
else:
|
|
36
50
|
type_map[collection[base_type]] = f"{collection_name}[{name}]"
|
|
37
51
|
return type_map
|
|
38
52
|
|
|
53
|
+
|
|
39
54
|
def generate_example_dict() -> Dict[str, Any]:
|
|
40
55
|
example_values = {
|
|
41
56
|
"str": "<Your type-str response here>",
|
|
@@ -54,12 +69,13 @@ def generate_example_dict() -> Dict[str, Any]:
|
|
|
54
69
|
example_dict[f"Dict[str,{key.split('[')[1]}"] = {value: value}
|
|
55
70
|
elif key.startswith("Dict"):
|
|
56
71
|
example_dict[key] = {value: value}
|
|
57
|
-
|
|
72
|
+
|
|
58
73
|
# Add example for Dict[Any,Any]
|
|
59
74
|
example_dict["Dict[Any,Any]"] = {"<key>": "<value>"}
|
|
60
|
-
|
|
75
|
+
|
|
61
76
|
return example_dict
|
|
62
77
|
|
|
78
|
+
|
|
63
79
|
base_type_examples = {
|
|
64
80
|
int: ("int", 42),
|
|
65
81
|
float: ("float", 3.14),
|
|
@@ -68,13 +84,23 @@ base_type_examples = {
|
|
|
68
84
|
Any: ("Any", "<Any value>"),
|
|
69
85
|
}
|
|
70
86
|
|
|
87
|
+
|
|
71
88
|
def get_type_string(type_hint):
|
|
72
89
|
origin = get_origin(type_hint)
|
|
73
90
|
args = get_args(type_hint)
|
|
74
91
|
|
|
75
92
|
if origin is None:
|
|
76
93
|
if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
|
|
77
|
-
|
|
94
|
+
# For Pydantic models, create a dictionary of field types
|
|
95
|
+
field_types = {}
|
|
96
|
+
for field_name, field_info in type_hint.model_fields.items():
|
|
97
|
+
field_type = get_type_string(field_info.annotation)
|
|
98
|
+
# Check for Literal type by looking at the origin
|
|
99
|
+
if get_origin(field_info.annotation) is Literal:
|
|
100
|
+
literal_args = get_args(field_info.annotation)
|
|
101
|
+
field_type = f"Literal[{repr(literal_args[0])}]"
|
|
102
|
+
field_types[field_name] = field_type
|
|
103
|
+
return f"{type_hint.__name__}({', '.join(f'{k}: {v}' for k, v in field_types.items())})"
|
|
78
104
|
else:
|
|
79
105
|
return base_type_examples.get(type_hint, ("Unknown", "unknown"))[0]
|
|
80
106
|
elif origin in (list, List):
|
|
@@ -89,40 +115,118 @@ def get_type_string(type_hint):
|
|
|
89
115
|
if len(non_none_types) == 1:
|
|
90
116
|
return f"Optional[{get_type_string(non_none_types[0])}]"
|
|
91
117
|
else:
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
# For unions of Pydantic models (like tool calls), show each variant
|
|
119
|
+
union_types = []
|
|
120
|
+
for t in non_none_types:
|
|
121
|
+
if isinstance(t, type) and issubclass(t, BaseModel):
|
|
122
|
+
# Include discriminator field if present
|
|
123
|
+
discriminator = None
|
|
124
|
+
for field_name, field_info in t.model_fields.items():
|
|
125
|
+
if get_origin(field_info.annotation) is Literal:
|
|
126
|
+
literal_args = get_args(field_info.annotation)
|
|
127
|
+
discriminator = f"{field_name}={repr(literal_args[0])}"
|
|
128
|
+
break
|
|
129
|
+
type_str = t.__name__
|
|
130
|
+
if discriminator:
|
|
131
|
+
type_str += f"({discriminator})"
|
|
132
|
+
union_types.append(type_str)
|
|
133
|
+
else:
|
|
134
|
+
union_types.append(get_type_string(t))
|
|
135
|
+
return f"Union[{', '.join(union_types)}]"
|
|
136
|
+
elif origin is Literal:
|
|
137
|
+
# Handle Literal type directly
|
|
138
|
+
return f"Literal[{repr(args[0])}]"
|
|
94
139
|
else:
|
|
95
140
|
return "Any"
|
|
96
141
|
|
|
142
|
+
|
|
97
143
|
def get_example_value(type_hint):
|
|
98
144
|
origin = get_origin(type_hint)
|
|
99
145
|
args = get_args(type_hint)
|
|
100
|
-
|
|
146
|
+
|
|
101
147
|
if origin is None:
|
|
102
148
|
if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
|
|
103
149
|
example = {}
|
|
150
|
+
union_docs = []
|
|
104
151
|
for field_name, field_info in type_hint.model_fields.items():
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
152
|
+
field_value, field_docs = get_example_value(field_info.annotation)
|
|
153
|
+
if field_docs:
|
|
154
|
+
union_docs.extend(field_docs)
|
|
155
|
+
|
|
156
|
+
# Handle literal fields by checking origin
|
|
157
|
+
if get_origin(field_info.annotation) is Literal:
|
|
158
|
+
literal_args = get_args(field_info.annotation)
|
|
159
|
+
field_value = literal_args[0]
|
|
160
|
+
|
|
161
|
+
# Include field description if available
|
|
162
|
+
if field_info.description:
|
|
163
|
+
example[field_name] = {
|
|
164
|
+
"value": field_value,
|
|
165
|
+
"description": field_info.description,
|
|
166
|
+
}
|
|
167
|
+
else:
|
|
168
|
+
example[field_name] = field_value
|
|
169
|
+
return example, union_docs
|
|
108
170
|
else:
|
|
109
|
-
return base_type_examples.get(type_hint, ("Unknown", "unknown"))[1]
|
|
171
|
+
return base_type_examples.get(type_hint, ("Unknown", "unknown"))[1], []
|
|
110
172
|
elif origin in (list, List):
|
|
111
|
-
|
|
173
|
+
value, docs = get_example_value(args[0])
|
|
174
|
+
return [value], docs
|
|
112
175
|
elif origin in (dict, Dict):
|
|
113
176
|
if not args or len(args) < 2:
|
|
114
177
|
warnings.warn(
|
|
115
178
|
f"Dictionary type hint {type_hint} missing type arguments. "
|
|
116
179
|
"Defaulting to Dict[str, Any].",
|
|
117
|
-
UserWarning
|
|
180
|
+
UserWarning,
|
|
118
181
|
)
|
|
119
|
-
return {"example_key": "<Any value>"} # Default for Dict[str, Any]
|
|
120
|
-
|
|
182
|
+
return {"example_key": "<Any value>"}, [] # Default for Dict[str, Any]
|
|
183
|
+
key_value, key_docs = get_example_value(args[0])
|
|
184
|
+
value_value, value_docs = get_example_value(args[1])
|
|
185
|
+
return {key_value: value_value}, key_docs + value_docs
|
|
121
186
|
elif origin is Union:
|
|
122
187
|
non_none_types = [t for t in args if t is not type(None)]
|
|
123
|
-
|
|
188
|
+
# For unions of tool calls, use the first one but preserve the discriminator
|
|
189
|
+
first_type = non_none_types[0]
|
|
190
|
+
union_docs = []
|
|
191
|
+
|
|
192
|
+
if all(
|
|
193
|
+
isinstance(t, type) and issubclass(t, BaseModel) for t in non_none_types
|
|
194
|
+
):
|
|
195
|
+
# Generate examples for all union variants
|
|
196
|
+
for t in non_none_types:
|
|
197
|
+
example = {}
|
|
198
|
+
for field_name, field_info in t.model_fields.items():
|
|
199
|
+
field_value, _ = get_example_value(field_info.annotation)
|
|
200
|
+
if get_origin(field_info.annotation) is Literal:
|
|
201
|
+
literal_args = get_args(field_info.annotation)
|
|
202
|
+
field_value = literal_args[0]
|
|
203
|
+
example[field_name] = field_value
|
|
204
|
+
union_docs.append(f"\nExample {t.__name__}:")
|
|
205
|
+
union_docs.append(json.dumps(example, indent=2))
|
|
206
|
+
|
|
207
|
+
# Return first type as main example
|
|
208
|
+
if isinstance(first_type, type) and issubclass(first_type, BaseModel):
|
|
209
|
+
example, _ = get_example_value(first_type)
|
|
210
|
+
# Ensure tool_type or other discriminator is preserved
|
|
211
|
+
for field_name, field_info in first_type.model_fields.items():
|
|
212
|
+
if get_origin(field_info.annotation) is Literal:
|
|
213
|
+
literal_args = get_args(field_info.annotation)
|
|
214
|
+
if (
|
|
215
|
+
isinstance(example[field_name], dict)
|
|
216
|
+
and "value" in example[field_name]
|
|
217
|
+
):
|
|
218
|
+
example[field_name]["value"] = literal_args[0]
|
|
219
|
+
else:
|
|
220
|
+
example[field_name] = literal_args[0]
|
|
221
|
+
return example, union_docs
|
|
222
|
+
main_example, docs = get_example_value(first_type)
|
|
223
|
+
return main_example, docs + union_docs
|
|
224
|
+
elif origin is Literal:
|
|
225
|
+
# Handle Literal type directly
|
|
226
|
+
return args[0], []
|
|
124
227
|
else:
|
|
125
|
-
return "<Unknown>"
|
|
228
|
+
return "<Unknown>", []
|
|
229
|
+
|
|
126
230
|
|
|
127
231
|
def add_json_instructions_to_messages(
|
|
128
232
|
system_message,
|
|
@@ -132,31 +236,53 @@ def add_json_instructions_to_messages(
|
|
|
132
236
|
) -> Tuple[str, str]:
|
|
133
237
|
if response_model:
|
|
134
238
|
type_hints = get_type_hints(response_model)
|
|
239
|
+
# print("Type hints", type_hints)
|
|
135
240
|
stringified_fields = {}
|
|
241
|
+
union_docs = []
|
|
136
242
|
for key, type_hint in type_hints.items():
|
|
137
|
-
|
|
243
|
+
# print("Key", key, "Type hint", type_hint)
|
|
244
|
+
example_value, docs = get_example_value(type_hint)
|
|
245
|
+
union_docs.extend(docs)
|
|
138
246
|
field = response_model.model_fields[key] # Updated for Pydantic v2
|
|
139
|
-
|
|
247
|
+
|
|
140
248
|
# Adjusted for Pydantic v2
|
|
141
|
-
field_description =
|
|
142
|
-
|
|
249
|
+
field_description = (
|
|
250
|
+
field.description if hasattr(field, "description") else None
|
|
251
|
+
)
|
|
252
|
+
|
|
143
253
|
if field_description:
|
|
144
254
|
stringified_fields[key] = (example_value, field_description)
|
|
145
255
|
else:
|
|
146
256
|
stringified_fields[key] = example_value
|
|
147
257
|
example_json = json.dumps(
|
|
148
|
-
{
|
|
149
|
-
|
|
258
|
+
{
|
|
259
|
+
k: v[0] if isinstance(v, tuple) else v
|
|
260
|
+
for k, v in stringified_fields.items()
|
|
261
|
+
},
|
|
262
|
+
indent=4,
|
|
150
263
|
)
|
|
151
264
|
description_comments = "\n".join(
|
|
152
|
-
f
|
|
265
|
+
f"// {k}: {v[1]}"
|
|
266
|
+
for k, v in stringified_fields.items()
|
|
267
|
+
if isinstance(v, tuple)
|
|
153
268
|
)
|
|
269
|
+
|
|
270
|
+
# print("Example JSON", example_json)
|
|
271
|
+
# print("Description comments", description_comments)
|
|
272
|
+
# print("Union documentation", "\n".join(union_docs))
|
|
273
|
+
# raise Exception("Stop here")
|
|
274
|
+
|
|
154
275
|
system_message += f"""\n\n
|
|
155
276
|
Please deliver your response in the following JSON format:
|
|
156
277
|
```json
|
|
157
278
|
{example_json}
|
|
158
279
|
```
|
|
159
280
|
{description_comments}
|
|
281
|
+
"""
|
|
282
|
+
if len(union_docs) != 0:
|
|
283
|
+
system_message += f"""\n\n
|
|
284
|
+
NOTE - the above example included a union type. Here are some additional examples for that union type:
|
|
285
|
+
{chr(10).join(union_docs)}
|
|
160
286
|
"""
|
|
161
287
|
if len(previously_failed_error_messages) != 0:
|
|
162
288
|
system_message += f"""\n\n
|
|
@@ -178,7 +304,10 @@ def inject_structured_output_instructions(
|
|
|
178
304
|
prev_system_message_content = messages[0]["content"]
|
|
179
305
|
prev_user_message_content = messages[1]["content"]
|
|
180
306
|
system_message, user_message = add_json_instructions_to_messages(
|
|
181
|
-
prev_system_message_content,
|
|
307
|
+
prev_system_message_content,
|
|
308
|
+
prev_user_message_content,
|
|
309
|
+
response_model,
|
|
310
|
+
previously_failed_error_messages,
|
|
182
311
|
)
|
|
183
312
|
messages[0]["content"] = system_message
|
|
184
313
|
messages[1]["content"] = user_message
|
|
@@ -9,11 +9,14 @@ from synth_ai.zyk.lms.caching.initialize import (
|
|
|
9
9
|
from synth_ai.zyk.lms.vendors.base import VendorBase
|
|
10
10
|
from synth_ai.zyk.lms.vendors.constants import SPECIAL_BASE_TEMPS
|
|
11
11
|
from synth_ai.zyk.lms.vendors.retries import BACKOFF_TOLERANCE, backoff
|
|
12
|
-
|
|
12
|
+
import groq
|
|
13
13
|
DEFAULT_EXCEPTIONS_TO_RETRY = (
|
|
14
14
|
pydantic_core._pydantic_core.ValidationError,
|
|
15
15
|
openai.APIConnectionError,
|
|
16
16
|
openai.APITimeoutError,
|
|
17
|
+
groq.InternalServerError,
|
|
18
|
+
groq.APITimeoutError,
|
|
19
|
+
groq.APIConnectionError,
|
|
17
20
|
)
|
|
18
21
|
|
|
19
22
|
|
|
@@ -25,12 +25,12 @@ synth_ai/zyk/lms/cost/monitor.py,sha256=cSKIvw6WdPZIRubADWxQoh1MdB40T8-jjgfNUeUH
|
|
|
25
25
|
synth_ai/zyk/lms/cost/statefulness.py,sha256=TOsuXL8IjtKOYJ2aJQF8TwJVqn_wQ7AIwJJmdhMye7U,36
|
|
26
26
|
synth_ai/zyk/lms/structured_outputs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
synth_ai/zyk/lms/structured_outputs/handler.py,sha256=2TixVuVs2w3Q3VB8n2DvNtJcJvUmQzAR1WfXn3FaX7M,13804
|
|
28
|
-
synth_ai/zyk/lms/structured_outputs/inject.py,sha256=
|
|
28
|
+
synth_ai/zyk/lms/structured_outputs/inject.py,sha256=Fy-zDeleRxOZ8ZRM6IuZ6CP2XZnMe4K2PEn4Q9c_KPY,11777
|
|
29
29
|
synth_ai/zyk/lms/structured_outputs/rehabilitate.py,sha256=_QOfnI1rJxIE9-zUMhC0PedCOr6y5m6WuGScDb5gcUo,7787
|
|
30
30
|
synth_ai/zyk/lms/vendors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
31
|
synth_ai/zyk/lms/vendors/base.py,sha256=bs27mNfrCjQ5NsmxWnKfxZmpoAa9mTNAFr9AnGVwqLk,552
|
|
32
32
|
synth_ai/zyk/lms/vendors/constants.py,sha256=zqCOyXZqo297wboR9EKVSkvpq6JCMSJyeso8HdZPKa4,102
|
|
33
|
-
synth_ai/zyk/lms/vendors/openai_standard.py,sha256=
|
|
33
|
+
synth_ai/zyk/lms/vendors/openai_standard.py,sha256=q-gyWs55IzaN91jERD8aPnuxLnG04ErEpNiL-6fqi1s,4892
|
|
34
34
|
synth_ai/zyk/lms/vendors/retries.py,sha256=m-WvAiPix9ovnO2S-m53Td5VZDWBVBFuHuSK9--OVxw,38
|
|
35
35
|
synth_ai/zyk/lms/vendors/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
synth_ai/zyk/lms/vendors/core/anthropic_api.py,sha256=fcb0uVlInADl50MNYMT-IimM9mzO19D8_mSg9Gqp92Q,6986
|
|
@@ -47,8 +47,8 @@ synth_ai/zyk/lms/vendors/supported/together.py,sha256=Ni_jBqqGPN0PkkY-Ew64s3gNKk
|
|
|
47
47
|
tests/test_agent.py,sha256=CjPPWuMWC_TzX1DkDald-bbAxgjXE-HPQvFhq2B--5k,22363
|
|
48
48
|
tests/test_recursive_structured_outputs.py,sha256=Ne-9XwnOxN7eSpGbNHOpegR-sRj589I84T6y8Z_4QnA,5781
|
|
49
49
|
tests/test_structured_outputs.py,sha256=J7sfbGZ7OeB5ONIKpcCTymyayNyAdFfGokC1bcUrSx0,3651
|
|
50
|
-
synth_ai-0.1.0.
|
|
51
|
-
synth_ai-0.1.0.
|
|
52
|
-
synth_ai-0.1.0.
|
|
53
|
-
synth_ai-0.1.0.
|
|
54
|
-
synth_ai-0.1.0.
|
|
50
|
+
synth_ai-0.1.0.dev15.dist-info/LICENSE,sha256=ynhjRQUfqA_RdGRATApfFA_fBAy9cno04sLtLUqxVFM,1069
|
|
51
|
+
synth_ai-0.1.0.dev15.dist-info/METADATA,sha256=4xPOHW4BWRROwbCDJxprZddjA7Xj9957cluvuAF6gFs,2773
|
|
52
|
+
synth_ai-0.1.0.dev15.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
53
|
+
synth_ai-0.1.0.dev15.dist-info/top_level.txt,sha256=5GzJO9j-KbJ_4ppxhmCUa_qdhHM4-9cHHNU76yAI8do,42
|
|
54
|
+
synth_ai-0.1.0.dev15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|