synth-ai 0.1.0.dev14__tar.gz → 0.1.0.dev15__tar.gz

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.

Files changed (61) hide show
  1. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/PKG-INFO +1 -1
  2. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/pyproject.toml +1 -1
  3. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/setup.py +1 -1
  4. synth_ai-0.1.0.dev15/synth_ai/zyk/lms/structured_outputs/inject.py +314 -0
  5. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai.egg-info/PKG-INFO +1 -1
  6. synth_ai-0.1.0.dev14/synth_ai/zyk/lms/structured_outputs/inject.py +0 -185
  7. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/LICENSE +0 -0
  8. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/README.md +0 -0
  9. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/private_tests/try_synth_sdk.py +0 -0
  10. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/public_tests/test_agent.py +0 -0
  11. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/public_tests/test_all_structured_outputs.py +0 -0
  12. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/public_tests/test_recursive_structured_outputs.py +0 -0
  13. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/public_tests/test_structured_outputs.py +0 -0
  14. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/public_tests/test_synth_sdk.py +0 -0
  15. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/setup.cfg +0 -0
  16. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/__init__.py +0 -0
  17. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/__init__.py +0 -0
  18. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/__init__.py +0 -0
  19. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/__init__.py +0 -0
  20. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/constants.py +0 -0
  21. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/dbs.py +0 -0
  22. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/ephemeral.py +0 -0
  23. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/handler.py +0 -0
  24. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/initialize.py +0 -0
  25. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/caching/persistent.py +0 -0
  26. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/config.py +0 -0
  27. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/core/__init__.py +0 -0
  28. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/core/all.py +0 -0
  29. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/core/exceptions.py +0 -0
  30. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/core/main.py +0 -0
  31. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/core/vendor_clients.py +0 -0
  32. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/cost/__init__.py +0 -0
  33. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/cost/monitor.py +0 -0
  34. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/cost/statefulness.py +0 -0
  35. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/structured_outputs/__init__.py +0 -0
  36. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/structured_outputs/handler.py +0 -0
  37. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/structured_outputs/rehabilitate.py +0 -0
  38. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/__init__.py +0 -0
  39. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/base.py +0 -0
  40. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/constants.py +0 -0
  41. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/core/__init__.py +0 -0
  42. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/core/anthropic_api.py +0 -0
  43. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/core/gemini_api.py +0 -0
  44. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/core/mistral_api.py +0 -0
  45. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/core/openai_api.py +0 -0
  46. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/local/__init__.py +0 -0
  47. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/local/ollama.py +0 -0
  48. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/openai_standard.py +0 -0
  49. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/retries.py +0 -0
  50. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/supported/__init__.py +0 -0
  51. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/supported/deepseek.py +0 -0
  52. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/supported/groq.py +0 -0
  53. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/supported/ollama.py +0 -0
  54. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai/zyk/lms/vendors/supported/together.py +0 -0
  55. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai.egg-info/SOURCES.txt +0 -0
  56. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai.egg-info/dependency_links.txt +0 -0
  57. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai.egg-info/requires.txt +0 -0
  58. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/synth_ai.egg-info/top_level.txt +0 -0
  59. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/tests/test_agent.py +0 -0
  60. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/tests/test_recursive_structured_outputs.py +0 -0
  61. {synth_ai-0.1.0.dev14 → synth_ai-0.1.0.dev15}/tests/test_structured_outputs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: synth-ai
3
- Version: 0.1.0.dev14
3
+ Version: 0.1.0.dev15
4
4
  Summary: Software for aiding the best and multiplying the will.
5
5
  Home-page: https://github.com/synth-laboratories/synth-ai
6
6
  Author: Josh Purtell
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-ai"
3
- version = "0.1.0.dev14"
3
+ version = "0.1.0.dev15"
4
4
  description = "Software for aiding the best and multiplying the will."
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Josh Purtell", email = "josh@usesynth.ai" }]
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="synth-ai",
5
- version="0.1.0.dev14",
5
+ version="0.1.0.dev15",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "openai",
@@ -0,0 +1,314 @@
1
+ import json
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
+ )
15
+ from pydantic import BaseModel
16
+ import warnings
17
+
18
+
19
+ def generate_type_map() -> Dict[Any, str]:
20
+ base_types = {
21
+ int: "int",
22
+ float: "float",
23
+ bool: "bool",
24
+ str: "str",
25
+ Any: "Any",
26
+ }
27
+
28
+ collection_types = {
29
+ List: "List",
30
+ Dict: "Dict",
31
+ Optional: "Optional",
32
+ }
33
+
34
+ type_map = {}
35
+ for base_type, name in base_types.items():
36
+ type_map[base_type] = name
37
+ for collection, collection_name in collection_types.items():
38
+ if collection is Optional:
39
+ type_map[Optional[base_type]] = name
40
+ elif collection is Dict:
41
+ # Handle generic Dict type
42
+ type_map[Dict] = "Dict[Any,Any]"
43
+ # Provide both key and value types for Dict
44
+ type_map[Dict[base_type, base_type]] = (
45
+ f"{collection_name}[{name},{name}]"
46
+ )
47
+ # Handle Dict[Any, Any] explicitly
48
+ type_map[Dict[Any, Any]] = "Dict[Any,Any]"
49
+ else:
50
+ type_map[collection[base_type]] = f"{collection_name}[{name}]"
51
+ return type_map
52
+
53
+
54
+ def generate_example_dict() -> Dict[str, Any]:
55
+ example_values = {
56
+ "str": "<Your type-str response here>",
57
+ "int": "<Your type-int response here>",
58
+ "float": "<Your type-float response here>",
59
+ "bool": "<Your type-bool response here>",
60
+ "Any": "<Your response here (infer the type from context)>",
61
+ }
62
+
63
+ example_dict = {}
64
+ for key, value in example_values.items():
65
+ example_dict[key] = value
66
+ example_dict[f"List[{key}]"] = [value]
67
+ example_dict[f"List[List[{key}]]"] = [[value]]
68
+ if key == "Dict[str,str]":
69
+ example_dict[f"Dict[str,{key.split('[')[1]}"] = {value: value}
70
+ elif key.startswith("Dict"):
71
+ example_dict[key] = {value: value}
72
+
73
+ # Add example for Dict[Any,Any]
74
+ example_dict["Dict[Any,Any]"] = {"<key>": "<value>"}
75
+
76
+ return example_dict
77
+
78
+
79
+ base_type_examples = {
80
+ int: ("int", 42),
81
+ float: ("float", 3.14),
82
+ bool: ("bool", True),
83
+ str: ("str", "example"),
84
+ Any: ("Any", "<Any value>"),
85
+ }
86
+
87
+
88
+ def get_type_string(type_hint):
89
+ origin = get_origin(type_hint)
90
+ args = get_args(type_hint)
91
+
92
+ if origin is None:
93
+ if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
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())})"
104
+ else:
105
+ return base_type_examples.get(type_hint, ("Unknown", "unknown"))[0]
106
+ elif origin in (list, List):
107
+ elem_type = get_type_string(args[0])
108
+ return f"List[{elem_type}]"
109
+ elif origin in (dict, Dict):
110
+ key_type = get_type_string(args[0])
111
+ value_type = get_type_string(args[1])
112
+ return f"Dict[{key_type}, {value_type}]"
113
+ elif origin is Union:
114
+ non_none_types = [t for t in args if t is not type(None)]
115
+ if len(non_none_types) == 1:
116
+ return f"Optional[{get_type_string(non_none_types[0])}]"
117
+ else:
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])}]"
139
+ else:
140
+ return "Any"
141
+
142
+
143
+ def get_example_value(type_hint):
144
+ origin = get_origin(type_hint)
145
+ args = get_args(type_hint)
146
+
147
+ if origin is None:
148
+ if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
149
+ example = {}
150
+ union_docs = []
151
+ for field_name, field_info in type_hint.model_fields.items():
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
170
+ else:
171
+ return base_type_examples.get(type_hint, ("Unknown", "unknown"))[1], []
172
+ elif origin in (list, List):
173
+ value, docs = get_example_value(args[0])
174
+ return [value], docs
175
+ elif origin in (dict, Dict):
176
+ if not args or len(args) < 2:
177
+ warnings.warn(
178
+ f"Dictionary type hint {type_hint} missing type arguments. "
179
+ "Defaulting to Dict[str, Any].",
180
+ UserWarning,
181
+ )
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
186
+ elif origin is Union:
187
+ non_none_types = [t for t in args if t is not type(None)]
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], []
227
+ else:
228
+ return "<Unknown>", []
229
+
230
+
231
+ def add_json_instructions_to_messages(
232
+ system_message,
233
+ user_message,
234
+ response_model: Optional[Type[BaseModel]] = None,
235
+ previously_failed_error_messages: List[str] = [],
236
+ ) -> Tuple[str, str]:
237
+ if response_model:
238
+ type_hints = get_type_hints(response_model)
239
+ # print("Type hints", type_hints)
240
+ stringified_fields = {}
241
+ union_docs = []
242
+ for key, type_hint in type_hints.items():
243
+ # print("Key", key, "Type hint", type_hint)
244
+ example_value, docs = get_example_value(type_hint)
245
+ union_docs.extend(docs)
246
+ field = response_model.model_fields[key] # Updated for Pydantic v2
247
+
248
+ # Adjusted for Pydantic v2
249
+ field_description = (
250
+ field.description if hasattr(field, "description") else None
251
+ )
252
+
253
+ if field_description:
254
+ stringified_fields[key] = (example_value, field_description)
255
+ else:
256
+ stringified_fields[key] = example_value
257
+ example_json = json.dumps(
258
+ {
259
+ k: v[0] if isinstance(v, tuple) else v
260
+ for k, v in stringified_fields.items()
261
+ },
262
+ indent=4,
263
+ )
264
+ description_comments = "\n".join(
265
+ f"// {k}: {v[1]}"
266
+ for k, v in stringified_fields.items()
267
+ if isinstance(v, tuple)
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
+
275
+ system_message += f"""\n\n
276
+ Please deliver your response in the following JSON format:
277
+ ```json
278
+ {example_json}
279
+ ```
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)}
286
+ """
287
+ if len(previously_failed_error_messages) != 0:
288
+ system_message += f"""\n\n
289
+ Please take special care to follow the format exactly.
290
+ Keep in mind the following:
291
+ - Always use double quotes for strings
292
+
293
+ Here are some error traces from previous attempts:
294
+ {previously_failed_error_messages}
295
+ """
296
+ return system_message, user_message
297
+
298
+
299
+ def inject_structured_output_instructions(
300
+ messages: List[Dict[str, str]],
301
+ response_model: Optional[Type[BaseModel]] = None,
302
+ previously_failed_error_messages: List[str] = [],
303
+ ) -> List[Dict[str, str]]:
304
+ prev_system_message_content = messages[0]["content"]
305
+ prev_user_message_content = messages[1]["content"]
306
+ system_message, user_message = add_json_instructions_to_messages(
307
+ prev_system_message_content,
308
+ prev_user_message_content,
309
+ response_model,
310
+ previously_failed_error_messages,
311
+ )
312
+ messages[0]["content"] = system_message
313
+ messages[1]["content"] = user_message
314
+ return messages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: synth-ai
3
- Version: 0.1.0.dev14
3
+ Version: 0.1.0.dev15
4
4
  Summary: Software for aiding the best and multiplying the will.
5
5
  Home-page: https://github.com/synth-laboratories/synth-ai
6
6
  Author: Josh Purtell
@@ -1,185 +0,0 @@
1
- import json
2
- from typing import Any, Dict, List, Optional, Tuple, Type, get_type_hints, get_args, get_origin, Union
3
- from pydantic import BaseModel
4
- import warnings
5
-
6
-
7
- def generate_type_map() -> Dict[Any, str]:
8
- base_types = {
9
- int: "int",
10
- float: "float",
11
- bool: "bool",
12
- str: "str",
13
- Any: "Any",
14
- }
15
-
16
- collection_types = {
17
- List: "List",
18
- Dict: "Dict",
19
- Optional: "Optional",
20
- }
21
-
22
- type_map = {}
23
- for base_type, name in base_types.items():
24
- type_map[base_type] = name
25
- for collection, collection_name in collection_types.items():
26
- if collection is Optional:
27
- type_map[Optional[base_type]] = name
28
- elif collection is Dict:
29
- # Handle generic Dict type
30
- type_map[Dict] = "Dict[Any,Any]"
31
- # Provide both key and value types for Dict
32
- type_map[Dict[base_type, base_type]] = f"{collection_name}[{name},{name}]"
33
- # Handle Dict[Any, Any] explicitly
34
- type_map[Dict[Any, Any]] = "Dict[Any,Any]"
35
- else:
36
- type_map[collection[base_type]] = f"{collection_name}[{name}]"
37
- return type_map
38
-
39
- def generate_example_dict() -> Dict[str, Any]:
40
- example_values = {
41
- "str": "<Your type-str response here>",
42
- "int": "<Your type-int response here>",
43
- "float": "<Your type-float response here>",
44
- "bool": "<Your type-bool response here>",
45
- "Any": "<Your response here (infer the type from context)>",
46
- }
47
-
48
- example_dict = {}
49
- for key, value in example_values.items():
50
- example_dict[key] = value
51
- example_dict[f"List[{key}]"] = [value]
52
- example_dict[f"List[List[{key}]]"] = [[value]]
53
- if key == "Dict[str,str]":
54
- example_dict[f"Dict[str,{key.split('[')[1]}"] = {value: value}
55
- elif key.startswith("Dict"):
56
- example_dict[key] = {value: value}
57
-
58
- # Add example for Dict[Any,Any]
59
- example_dict["Dict[Any,Any]"] = {"<key>": "<value>"}
60
-
61
- return example_dict
62
-
63
- base_type_examples = {
64
- int: ("int", 42),
65
- float: ("float", 3.14),
66
- bool: ("bool", True),
67
- str: ("str", "example"),
68
- Any: ("Any", "<Any value>"),
69
- }
70
-
71
- def get_type_string(type_hint):
72
- origin = get_origin(type_hint)
73
- args = get_args(type_hint)
74
-
75
- if origin is None:
76
- if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
77
- return type_hint.__name__
78
- else:
79
- return base_type_examples.get(type_hint, ("Unknown", "unknown"))[0]
80
- elif origin in (list, List):
81
- elem_type = get_type_string(args[0])
82
- return f"List[{elem_type}]"
83
- elif origin in (dict, Dict):
84
- key_type = get_type_string(args[0])
85
- value_type = get_type_string(args[1])
86
- return f"Dict[{key_type}, {value_type}]"
87
- elif origin is Union:
88
- non_none_types = [t for t in args if t is not type(None)]
89
- if len(non_none_types) == 1:
90
- return f"Optional[{get_type_string(non_none_types[0])}]"
91
- else:
92
- union_types = ", ".join(get_type_string(t) for t in non_none_types)
93
- return f"Union[{union_types}]"
94
- else:
95
- return "Any"
96
-
97
- def get_example_value(type_hint):
98
- origin = get_origin(type_hint)
99
- args = get_args(type_hint)
100
-
101
- if origin is None:
102
- if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
103
- example = {}
104
- for field_name, field_info in type_hint.model_fields.items():
105
- # Updated attribute from type_ to annotation
106
- example[field_name] = get_example_value(field_info.annotation)
107
- return example
108
- else:
109
- return base_type_examples.get(type_hint, ("Unknown", "unknown"))[1]
110
- elif origin in (list, List):
111
- return [get_example_value(args[0])]
112
- elif origin in (dict, Dict):
113
- if not args or len(args) < 2:
114
- warnings.warn(
115
- f"Dictionary type hint {type_hint} missing type arguments. "
116
- "Defaulting to Dict[str, Any].",
117
- UserWarning
118
- )
119
- return {"example_key": "<Any value>"} # Default for Dict[str, Any]
120
- return {get_example_value(args[0]): get_example_value(args[1])}
121
- elif origin is Union:
122
- non_none_types = [t for t in args if t is not type(None)]
123
- return get_example_value(non_none_types[0])
124
- else:
125
- return "<Unknown>"
126
-
127
- def add_json_instructions_to_messages(
128
- system_message,
129
- user_message,
130
- response_model: Optional[Type[BaseModel]] = None,
131
- previously_failed_error_messages: List[str] = [],
132
- ) -> Tuple[str, str]:
133
- if response_model:
134
- type_hints = get_type_hints(response_model)
135
- stringified_fields = {}
136
- for key, type_hint in type_hints.items():
137
- example_value = get_example_value(type_hint)
138
- field = response_model.model_fields[key] # Updated for Pydantic v2
139
-
140
- # Adjusted for Pydantic v2
141
- field_description = field.description if hasattr(field, 'description') else None
142
-
143
- if field_description:
144
- stringified_fields[key] = (example_value, field_description)
145
- else:
146
- stringified_fields[key] = example_value
147
- example_json = json.dumps(
148
- {k: v[0] if isinstance(v, tuple) else v for k, v in stringified_fields.items()},
149
- indent=4
150
- )
151
- description_comments = "\n".join(
152
- f'// {k}: {v[1]}' for k, v in stringified_fields.items() if isinstance(v, tuple)
153
- )
154
- system_message += f"""\n\n
155
- Please deliver your response in the following JSON format:
156
- ```json
157
- {example_json}
158
- ```
159
- {description_comments}
160
- """
161
- if len(previously_failed_error_messages) != 0:
162
- system_message += f"""\n\n
163
- Please take special care to follow the format exactly.
164
- Keep in mind the following:
165
- - Always use double quotes for strings
166
-
167
- Here are some error traces from previous attempts:
168
- {previously_failed_error_messages}
169
- """
170
- return system_message, user_message
171
-
172
-
173
- def inject_structured_output_instructions(
174
- messages: List[Dict[str, str]],
175
- response_model: Optional[Type[BaseModel]] = None,
176
- previously_failed_error_messages: List[str] = [],
177
- ) -> List[Dict[str, str]]:
178
- prev_system_message_content = messages[0]["content"]
179
- prev_user_message_content = messages[1]["content"]
180
- system_message, user_message = add_json_instructions_to_messages(
181
- prev_system_message_content, prev_user_message_content, response_model, previously_failed_error_messages
182
- )
183
- messages[0]["content"] = system_message
184
- messages[1]["content"] = user_message
185
- return messages
File without changes
File without changes
File without changes