synth-ai 0.1.0.dev38__py3-none-any.whl → 0.1.0.dev49__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.
- synth_ai/__init__.py +3 -1
- {synth_ai-0.1.0.dev38.dist-info → synth_ai-0.1.0.dev49.dist-info}/METADATA +12 -11
- synth_ai-0.1.0.dev49.dist-info/RECORD +6 -0
- {synth_ai-0.1.0.dev38.dist-info → synth_ai-0.1.0.dev49.dist-info}/WHEEL +1 -1
- synth_ai-0.1.0.dev49.dist-info/top_level.txt +1 -0
- private_tests/try_synth_sdk.py +0 -1
- public_tests/test_agent.py +0 -538
- public_tests/test_all_structured_outputs.py +0 -196
- public_tests/test_anthropic_structured_outputs.py +0 -0
- public_tests/test_deepseek_structured_outputs.py +0 -0
- public_tests/test_deepseek_tools.py +0 -64
- public_tests/test_gemini_output.py +0 -188
- public_tests/test_gemini_structured_outputs.py +0 -106
- public_tests/test_models.py +0 -183
- public_tests/test_openai_structured_outputs.py +0 -106
- public_tests/test_reasoning_effort.py +0 -75
- public_tests/test_reasoning_models.py +0 -92
- public_tests/test_recursive_structured_outputs.py +0 -180
- public_tests/test_structured.py +0 -137
- public_tests/test_structured_outputs.py +0 -109
- public_tests/test_synth_sdk.py +0 -384
- public_tests/test_text.py +0 -160
- public_tests/test_tools.py +0 -319
- synth_ai/zyk/__init__.py +0 -3
- synth_ai/zyk/lms/__init__.py +0 -0
- synth_ai/zyk/lms/caching/__init__.py +0 -0
- synth_ai/zyk/lms/caching/constants.py +0 -1
- synth_ai/zyk/lms/caching/dbs.py +0 -0
- synth_ai/zyk/lms/caching/ephemeral.py +0 -72
- synth_ai/zyk/lms/caching/handler.py +0 -142
- synth_ai/zyk/lms/caching/initialize.py +0 -13
- synth_ai/zyk/lms/caching/persistent.py +0 -83
- synth_ai/zyk/lms/config.py +0 -8
- synth_ai/zyk/lms/core/__init__.py +0 -0
- synth_ai/zyk/lms/core/all.py +0 -47
- synth_ai/zyk/lms/core/exceptions.py +0 -9
- synth_ai/zyk/lms/core/main.py +0 -314
- synth_ai/zyk/lms/core/vendor_clients.py +0 -85
- synth_ai/zyk/lms/cost/__init__.py +0 -0
- synth_ai/zyk/lms/cost/monitor.py +0 -1
- synth_ai/zyk/lms/cost/statefulness.py +0 -1
- synth_ai/zyk/lms/structured_outputs/__init__.py +0 -0
- synth_ai/zyk/lms/structured_outputs/handler.py +0 -442
- synth_ai/zyk/lms/structured_outputs/inject.py +0 -314
- synth_ai/zyk/lms/structured_outputs/rehabilitate.py +0 -187
- synth_ai/zyk/lms/tools/base.py +0 -104
- synth_ai/zyk/lms/vendors/__init__.py +0 -0
- synth_ai/zyk/lms/vendors/base.py +0 -31
- synth_ai/zyk/lms/vendors/constants.py +0 -22
- synth_ai/zyk/lms/vendors/core/__init__.py +0 -0
- synth_ai/zyk/lms/vendors/core/anthropic_api.py +0 -413
- synth_ai/zyk/lms/vendors/core/gemini_api.py +0 -306
- synth_ai/zyk/lms/vendors/core/mistral_api.py +0 -327
- synth_ai/zyk/lms/vendors/core/openai_api.py +0 -185
- synth_ai/zyk/lms/vendors/local/__init__.py +0 -0
- synth_ai/zyk/lms/vendors/local/ollama.py +0 -0
- synth_ai/zyk/lms/vendors/openai_standard.py +0 -375
- synth_ai/zyk/lms/vendors/retries.py +0 -3
- synth_ai/zyk/lms/vendors/supported/__init__.py +0 -0
- synth_ai/zyk/lms/vendors/supported/deepseek.py +0 -73
- synth_ai/zyk/lms/vendors/supported/groq.py +0 -16
- synth_ai/zyk/lms/vendors/supported/ollama.py +0 -14
- synth_ai/zyk/lms/vendors/supported/together.py +0 -11
- synth_ai-0.1.0.dev38.dist-info/RECORD +0 -67
- synth_ai-0.1.0.dev38.dist-info/top_level.txt +0 -4
- tests/test_agent.py +0 -538
- tests/test_recursive_structured_outputs.py +0 -180
- tests/test_structured_outputs.py +0 -100
- {synth_ai-0.1.0.dev38.dist-info → synth_ai-0.1.0.dev49.dist-info}/licenses/LICENSE +0 -0
@@ -1,413 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from typing import Any, Dict, List, Optional, Tuple, Type
|
3
|
-
|
4
|
-
import anthropic
|
5
|
-
import pydantic
|
6
|
-
from pydantic import BaseModel
|
7
|
-
|
8
|
-
from synth_ai.zyk.lms.caching.initialize import (
|
9
|
-
get_cache_handler,
|
10
|
-
)
|
11
|
-
from synth_ai.zyk.lms.tools.base import BaseTool
|
12
|
-
from synth_ai.zyk.lms.vendors.base import BaseLMResponse, VendorBase
|
13
|
-
from synth_ai.zyk.lms.vendors.constants import SPECIAL_BASE_TEMPS
|
14
|
-
from synth_ai.zyk.lms.vendors.core.openai_api import OpenAIStructuredOutputClient
|
15
|
-
|
16
|
-
ANTHROPIC_EXCEPTIONS_TO_RETRY: Tuple[Type[Exception], ...] = (anthropic.APIError,)
|
17
|
-
|
18
|
-
|
19
|
-
sonnet_37_budgets = {
|
20
|
-
"high": 8000,
|
21
|
-
"medium": 4000,
|
22
|
-
"low": 1000,
|
23
|
-
}
|
24
|
-
|
25
|
-
class AnthropicAPI(VendorBase):
|
26
|
-
used_for_structured_outputs: bool = True
|
27
|
-
exceptions_to_retry: Tuple = ANTHROPIC_EXCEPTIONS_TO_RETRY
|
28
|
-
sync_client: Any
|
29
|
-
async_client: Any
|
30
|
-
|
31
|
-
def __init__(
|
32
|
-
self,
|
33
|
-
exceptions_to_retry: Tuple[
|
34
|
-
Type[Exception], ...
|
35
|
-
] = ANTHROPIC_EXCEPTIONS_TO_RETRY,
|
36
|
-
used_for_structured_outputs: bool = False,
|
37
|
-
reasoning_effort: str = "high",
|
38
|
-
):
|
39
|
-
self.sync_client = anthropic.Anthropic()
|
40
|
-
self.async_client = anthropic.AsyncAnthropic()
|
41
|
-
self.used_for_structured_outputs = used_for_structured_outputs
|
42
|
-
self.exceptions_to_retry = exceptions_to_retry
|
43
|
-
self._openai_fallback = None
|
44
|
-
self.reasoning_effort = reasoning_effort
|
45
|
-
|
46
|
-
# @backoff.on_exception(
|
47
|
-
# backoff.expo,
|
48
|
-
# exceptions_to_retry,
|
49
|
-
# max_tries=BACKOFF_TOLERANCE,
|
50
|
-
# on_giveup=lambda e: print(e),
|
51
|
-
# )
|
52
|
-
async def _hit_api_async(
|
53
|
-
self,
|
54
|
-
model: str,
|
55
|
-
messages: List[Dict[str, Any]],
|
56
|
-
lm_config: Dict[str, Any],
|
57
|
-
use_ephemeral_cache_only: bool = False,
|
58
|
-
reasoning_effort: str = "high",
|
59
|
-
tools: Optional[List[BaseTool]] = None,
|
60
|
-
**vendor_params: Dict[str, Any],
|
61
|
-
) -> BaseLMResponse:
|
62
|
-
assert (
|
63
|
-
lm_config.get("response_model", None) is None
|
64
|
-
), "response_model is not supported for standard calls"
|
65
|
-
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
66
|
-
cache_result = used_cache_handler.hit_managed_cache(
|
67
|
-
model, messages, lm_config=lm_config, tools=tools, reasoning_effort=reasoning_effort
|
68
|
-
)
|
69
|
-
if cache_result:
|
70
|
-
return cache_result
|
71
|
-
|
72
|
-
# Common API parameters
|
73
|
-
api_params = {
|
74
|
-
"system": messages[0]["content"],
|
75
|
-
"messages": messages[1:],
|
76
|
-
"model": model,
|
77
|
-
"max_tokens": lm_config.get("max_tokens", 4096),
|
78
|
-
"temperature": lm_config.get(
|
79
|
-
"temperature", SPECIAL_BASE_TEMPS.get(model, 0)
|
80
|
-
),
|
81
|
-
}
|
82
|
-
|
83
|
-
# Add tools if provided
|
84
|
-
if tools:
|
85
|
-
api_params["tools"] = []
|
86
|
-
for tool in tools:
|
87
|
-
if isinstance(tool, BaseTool):
|
88
|
-
api_params["tools"].append(tool.to_anthropic_tool())
|
89
|
-
else:
|
90
|
-
api_params["tools"].append(tool)
|
91
|
-
|
92
|
-
# Only try to add thinking if supported by the SDK
|
93
|
-
try:
|
94
|
-
import inspect
|
95
|
-
|
96
|
-
create_sig = inspect.signature(self.async_client.messages.create)
|
97
|
-
if "thinking" in create_sig.parameters and "claude-3-7" in model:
|
98
|
-
if reasoning_effort in ["high", "medium"]:
|
99
|
-
budget = sonnet_37_budgets[reasoning_effort]
|
100
|
-
api_params["thinking"] = {
|
101
|
-
"type": "enabled",
|
102
|
-
"budget_tokens": budget,
|
103
|
-
}
|
104
|
-
api_params["max_tokens"] = budget+4096
|
105
|
-
api_params["temperature"] = 1
|
106
|
-
except (ImportError, AttributeError, TypeError):
|
107
|
-
pass
|
108
|
-
|
109
|
-
# Make the API call
|
110
|
-
response = await self.async_client.messages.create(**api_params)
|
111
|
-
|
112
|
-
# Extract text content and tool calls
|
113
|
-
raw_response = ""
|
114
|
-
tool_calls = []
|
115
|
-
|
116
|
-
for content in response.content:
|
117
|
-
if content.type == "text":
|
118
|
-
raw_response += content.text
|
119
|
-
elif content.type == "tool_use":
|
120
|
-
tool_calls.append(
|
121
|
-
{
|
122
|
-
"id": content.id,
|
123
|
-
"type": "function",
|
124
|
-
"function": {
|
125
|
-
"name": content.name,
|
126
|
-
"arguments": json.dumps(content.input),
|
127
|
-
},
|
128
|
-
}
|
129
|
-
)
|
130
|
-
|
131
|
-
lm_response = BaseLMResponse(
|
132
|
-
raw_response=raw_response,
|
133
|
-
structured_output=None,
|
134
|
-
tool_calls=tool_calls if tool_calls else None,
|
135
|
-
)
|
136
|
-
|
137
|
-
used_cache_handler.add_to_managed_cache(
|
138
|
-
model, messages, lm_config=lm_config, output=lm_response, tools=tools, reasoning_effort=reasoning_effort
|
139
|
-
)
|
140
|
-
return lm_response
|
141
|
-
|
142
|
-
# @backoff.on_exception(
|
143
|
-
# backoff.expo,
|
144
|
-
# exceptions_to_retry,
|
145
|
-
# max_tries=BACKOFF_TOLERANCE,
|
146
|
-
# on_giveup=lambda e: print(e),
|
147
|
-
# )
|
148
|
-
def _hit_api_sync(
|
149
|
-
self,
|
150
|
-
model: str,
|
151
|
-
messages: List[Dict[str, Any]],
|
152
|
-
lm_config: Dict[str, Any],
|
153
|
-
use_ephemeral_cache_only: bool = False,
|
154
|
-
reasoning_effort: str = "high",
|
155
|
-
tools: Optional[List[BaseTool]] = None,
|
156
|
-
**vendor_params: Dict[str, Any],
|
157
|
-
) -> BaseLMResponse:
|
158
|
-
assert (
|
159
|
-
lm_config.get("response_model", None) is None
|
160
|
-
), "response_model is not supported for standard calls"
|
161
|
-
used_cache_handler = get_cache_handler(
|
162
|
-
use_ephemeral_cache_only=use_ephemeral_cache_only
|
163
|
-
)
|
164
|
-
cache_result = used_cache_handler.hit_managed_cache(
|
165
|
-
model, messages, lm_config=lm_config, tools=tools, reasoning_effort=reasoning_effort
|
166
|
-
)
|
167
|
-
if cache_result:
|
168
|
-
return cache_result
|
169
|
-
|
170
|
-
# Common API parameters
|
171
|
-
api_params = {
|
172
|
-
"system": messages[0]["content"],
|
173
|
-
"messages": messages[1:],
|
174
|
-
"model": model,
|
175
|
-
"max_tokens": lm_config.get("max_tokens", 4096),
|
176
|
-
"temperature": lm_config.get(
|
177
|
-
"temperature", SPECIAL_BASE_TEMPS.get(model, 0)
|
178
|
-
),
|
179
|
-
}
|
180
|
-
|
181
|
-
# Add tools if provided
|
182
|
-
if tools:
|
183
|
-
api_params["tools"] = []
|
184
|
-
for tool in tools:
|
185
|
-
if isinstance(tool, BaseTool):
|
186
|
-
api_params["tools"].append(tool.to_anthropic_tool())
|
187
|
-
else:
|
188
|
-
api_params["tools"].append(tool)
|
189
|
-
|
190
|
-
# Only try to add thinking if supported by the SDK
|
191
|
-
try:
|
192
|
-
import inspect
|
193
|
-
|
194
|
-
create_sig = inspect.signature(self.sync_client.messages.create)
|
195
|
-
if "thinking" in create_sig.parameters and "claude-3-7" in model:
|
196
|
-
api_params["temperature"] = 1
|
197
|
-
if reasoning_effort in ["high", "medium"]:
|
198
|
-
budgets = sonnet_37_budgets
|
199
|
-
budget = budgets[reasoning_effort]
|
200
|
-
api_params["thinking"] = {
|
201
|
-
"type": "enabled",
|
202
|
-
"budget_tokens": budget,
|
203
|
-
}
|
204
|
-
api_params["max_tokens"] = budget+4096
|
205
|
-
api_params["temperature"] = 1
|
206
|
-
except (ImportError, AttributeError, TypeError):
|
207
|
-
pass
|
208
|
-
|
209
|
-
# Make the API call
|
210
|
-
response = self.sync_client.messages.create(**api_params)
|
211
|
-
|
212
|
-
# Extract text content and tool calls
|
213
|
-
raw_response = ""
|
214
|
-
tool_calls = []
|
215
|
-
|
216
|
-
for content in response.content:
|
217
|
-
if content.type == "text":
|
218
|
-
raw_response += content.text
|
219
|
-
elif content.type == "tool_use":
|
220
|
-
tool_calls.append(
|
221
|
-
{
|
222
|
-
"id": content.id,
|
223
|
-
"type": "function",
|
224
|
-
"function": {
|
225
|
-
"name": content.name,
|
226
|
-
"arguments": json.dumps(content.input),
|
227
|
-
},
|
228
|
-
}
|
229
|
-
)
|
230
|
-
|
231
|
-
lm_response = BaseLMResponse(
|
232
|
-
raw_response=raw_response,
|
233
|
-
structured_output=None,
|
234
|
-
tool_calls=tool_calls if tool_calls else None,
|
235
|
-
)
|
236
|
-
|
237
|
-
used_cache_handler.add_to_managed_cache(
|
238
|
-
model, messages, lm_config=lm_config, output=lm_response, tools=tools, reasoning_effort=reasoning_effort
|
239
|
-
)
|
240
|
-
return lm_response
|
241
|
-
|
242
|
-
async def _hit_api_async_structured_output(
|
243
|
-
self,
|
244
|
-
model: str,
|
245
|
-
messages: List[Dict[str, Any]],
|
246
|
-
response_model: BaseModel,
|
247
|
-
temperature: float,
|
248
|
-
use_ephemeral_cache_only: bool = False,
|
249
|
-
reasoning_effort: str = "high",
|
250
|
-
**vendor_params: Dict[str, Any],
|
251
|
-
) -> BaseLMResponse:
|
252
|
-
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
253
|
-
lm_config = {"temperature": temperature, "response_model": response_model}
|
254
|
-
cache_result = used_cache_handler.hit_managed_cache(
|
255
|
-
model=model,
|
256
|
-
messages=messages,
|
257
|
-
lm_config=lm_config,
|
258
|
-
reasoning_effort=reasoning_effort,
|
259
|
-
)
|
260
|
-
if cache_result:
|
261
|
-
return cache_result
|
262
|
-
|
263
|
-
try:
|
264
|
-
# First try with Anthropic
|
265
|
-
reasoning_effort = vendor_params.get("reasoning_effort", reasoning_effort)
|
266
|
-
if "claude-3-7" in model:
|
267
|
-
|
268
|
-
#if reasoning_effort in ["high", "medium"]:
|
269
|
-
budgets = sonnet_37_budgets
|
270
|
-
budget = budgets[reasoning_effort]
|
271
|
-
max_tokens = budget+4096
|
272
|
-
temperature = 1
|
273
|
-
|
274
|
-
response = await self.async_client.messages.create(
|
275
|
-
system=messages[0]["content"],
|
276
|
-
messages=messages[1:],
|
277
|
-
model=model,
|
278
|
-
max_tokens=max_tokens,
|
279
|
-
thinking={"type": "enabled", "budget_tokens": budget},
|
280
|
-
temperature=temperature,
|
281
|
-
)
|
282
|
-
else:
|
283
|
-
response = await self.async_client.messages.create(
|
284
|
-
system=messages[0]["content"],
|
285
|
-
messages=messages[1:],
|
286
|
-
model=model,
|
287
|
-
max_tokens=max_tokens,
|
288
|
-
temperature=temperature,
|
289
|
-
)
|
290
|
-
result = response.content[0].text
|
291
|
-
parsed = json.loads(result)
|
292
|
-
lm_response = BaseLMResponse(
|
293
|
-
raw_response="",
|
294
|
-
structured_output=response_model(**parsed),
|
295
|
-
tool_calls=None,
|
296
|
-
)
|
297
|
-
used_cache_handler.add_to_managed_cache(
|
298
|
-
model=model,
|
299
|
-
messages=messages,
|
300
|
-
lm_config=lm_config,
|
301
|
-
output=lm_response,
|
302
|
-
reasoning_effort=reasoning_effort,
|
303
|
-
)
|
304
|
-
return lm_response
|
305
|
-
except (json.JSONDecodeError, pydantic.ValidationError):
|
306
|
-
# If Anthropic fails, fallback to OpenAI
|
307
|
-
if self._openai_fallback is None:
|
308
|
-
self._openai_fallback = OpenAIStructuredOutputClient()
|
309
|
-
return await self._openai_fallback._hit_api_async_structured_output(
|
310
|
-
model="gpt-4o", # Fallback to GPT-4
|
311
|
-
messages=messages,
|
312
|
-
response_model=response_model,
|
313
|
-
temperature=temperature,
|
314
|
-
use_ephemeral_cache_only=use_ephemeral_cache_only,
|
315
|
-
)
|
316
|
-
|
317
|
-
def _hit_api_sync_structured_output(
|
318
|
-
self,
|
319
|
-
model: str,
|
320
|
-
messages: List[Dict[str, Any]],
|
321
|
-
response_model: BaseModel,
|
322
|
-
temperature: float,
|
323
|
-
use_ephemeral_cache_only: bool = False,
|
324
|
-
reasoning_effort: str = "high",
|
325
|
-
**vendor_params: Dict[str, Any],
|
326
|
-
) -> BaseLMResponse:
|
327
|
-
used_cache_handler = get_cache_handler(use_ephemeral_cache_only)
|
328
|
-
lm_config = {"temperature": temperature, "response_model": response_model}
|
329
|
-
cache_result = used_cache_handler.hit_managed_cache(
|
330
|
-
model=model,
|
331
|
-
messages=messages,
|
332
|
-
lm_config=lm_config,
|
333
|
-
reasoning_effort=reasoning_effort,
|
334
|
-
)
|
335
|
-
if cache_result:
|
336
|
-
return cache_result
|
337
|
-
|
338
|
-
try:
|
339
|
-
# First try with Anthropic
|
340
|
-
reasoning_effort = vendor_params.get("reasoning_effort", reasoning_effort)
|
341
|
-
import time
|
342
|
-
|
343
|
-
if "claude-3-7" in model:
|
344
|
-
if reasoning_effort in ["high", "medium"]:
|
345
|
-
budgets = sonnet_37_budgets
|
346
|
-
budget = budgets[reasoning_effort]
|
347
|
-
max_tokens = budget+4096
|
348
|
-
temperature = 1
|
349
|
-
response = self.sync_client.messages.create(
|
350
|
-
system=messages[0]["content"],
|
351
|
-
messages=messages[1:],
|
352
|
-
model=model,
|
353
|
-
max_tokens=max_tokens,
|
354
|
-
temperature=temperature,
|
355
|
-
thinking={"type": "enabled", "budget_tokens": budget},
|
356
|
-
)
|
357
|
-
else:
|
358
|
-
response = self.sync_client.messages.create(
|
359
|
-
system=messages[0]["content"],
|
360
|
-
messages=messages[1:],
|
361
|
-
model=model,
|
362
|
-
max_tokens=max_tokens,
|
363
|
-
temperature=temperature,
|
364
|
-
)
|
365
|
-
# print("Time taken for API call", time.time() - t)
|
366
|
-
result = response.content[0].text
|
367
|
-
# Try to parse the result as JSON
|
368
|
-
parsed = json.loads(result)
|
369
|
-
lm_response = BaseLMResponse(
|
370
|
-
raw_response="",
|
371
|
-
structured_output=response_model(**parsed),
|
372
|
-
tool_calls=None,
|
373
|
-
)
|
374
|
-
used_cache_handler.add_to_managed_cache(
|
375
|
-
model=model,
|
376
|
-
messages=messages,
|
377
|
-
lm_config=lm_config,
|
378
|
-
output=lm_response,
|
379
|
-
reasoning_effort=reasoning_effort,
|
380
|
-
)
|
381
|
-
return lm_response
|
382
|
-
except (json.JSONDecodeError, pydantic.ValidationError):
|
383
|
-
# If Anthropic fails, fallback to OpenAI
|
384
|
-
print("WARNING - Falling back to OpenAI - THIS IS SLOW")
|
385
|
-
if self._openai_fallback is None:
|
386
|
-
self._openai_fallback = OpenAIStructuredOutputClient()
|
387
|
-
return self._openai_fallback._hit_api_sync_structured_output(
|
388
|
-
model="gpt-4o", # Fallback to GPT-4
|
389
|
-
messages=messages,
|
390
|
-
response_model=response_model,
|
391
|
-
temperature=temperature,
|
392
|
-
use_ephemeral_cache_only=use_ephemeral_cache_only,
|
393
|
-
)
|
394
|
-
|
395
|
-
async def _process_call_async(
|
396
|
-
self,
|
397
|
-
messages: List[Dict[str, Any]],
|
398
|
-
model: str,
|
399
|
-
response_model: BaseModel,
|
400
|
-
api_call_method,
|
401
|
-
temperature: float = 0.0,
|
402
|
-
use_ephemeral_cache_only: bool = False,
|
403
|
-
vendor_params: Dict[str, Any] = None,
|
404
|
-
) -> BaseModel:
|
405
|
-
vendor_params = vendor_params or {}
|
406
|
-
# Each vendor can filter parameters they support
|
407
|
-
return await api_call_method(
|
408
|
-
messages=messages,
|
409
|
-
model=model,
|
410
|
-
temperature=temperature,
|
411
|
-
use_ephemeral_cache_only=use_ephemeral_cache_only,
|
412
|
-
**vendor_params, # Pass all vendor-specific params
|
413
|
-
)
|