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.
Files changed (69) hide show
  1. synth_ai/__init__.py +3 -1
  2. {synth_ai-0.1.0.dev38.dist-info → synth_ai-0.1.0.dev49.dist-info}/METADATA +12 -11
  3. synth_ai-0.1.0.dev49.dist-info/RECORD +6 -0
  4. {synth_ai-0.1.0.dev38.dist-info → synth_ai-0.1.0.dev49.dist-info}/WHEEL +1 -1
  5. synth_ai-0.1.0.dev49.dist-info/top_level.txt +1 -0
  6. private_tests/try_synth_sdk.py +0 -1
  7. public_tests/test_agent.py +0 -538
  8. public_tests/test_all_structured_outputs.py +0 -196
  9. public_tests/test_anthropic_structured_outputs.py +0 -0
  10. public_tests/test_deepseek_structured_outputs.py +0 -0
  11. public_tests/test_deepseek_tools.py +0 -64
  12. public_tests/test_gemini_output.py +0 -188
  13. public_tests/test_gemini_structured_outputs.py +0 -106
  14. public_tests/test_models.py +0 -183
  15. public_tests/test_openai_structured_outputs.py +0 -106
  16. public_tests/test_reasoning_effort.py +0 -75
  17. public_tests/test_reasoning_models.py +0 -92
  18. public_tests/test_recursive_structured_outputs.py +0 -180
  19. public_tests/test_structured.py +0 -137
  20. public_tests/test_structured_outputs.py +0 -109
  21. public_tests/test_synth_sdk.py +0 -384
  22. public_tests/test_text.py +0 -160
  23. public_tests/test_tools.py +0 -319
  24. synth_ai/zyk/__init__.py +0 -3
  25. synth_ai/zyk/lms/__init__.py +0 -0
  26. synth_ai/zyk/lms/caching/__init__.py +0 -0
  27. synth_ai/zyk/lms/caching/constants.py +0 -1
  28. synth_ai/zyk/lms/caching/dbs.py +0 -0
  29. synth_ai/zyk/lms/caching/ephemeral.py +0 -72
  30. synth_ai/zyk/lms/caching/handler.py +0 -142
  31. synth_ai/zyk/lms/caching/initialize.py +0 -13
  32. synth_ai/zyk/lms/caching/persistent.py +0 -83
  33. synth_ai/zyk/lms/config.py +0 -8
  34. synth_ai/zyk/lms/core/__init__.py +0 -0
  35. synth_ai/zyk/lms/core/all.py +0 -47
  36. synth_ai/zyk/lms/core/exceptions.py +0 -9
  37. synth_ai/zyk/lms/core/main.py +0 -314
  38. synth_ai/zyk/lms/core/vendor_clients.py +0 -85
  39. synth_ai/zyk/lms/cost/__init__.py +0 -0
  40. synth_ai/zyk/lms/cost/monitor.py +0 -1
  41. synth_ai/zyk/lms/cost/statefulness.py +0 -1
  42. synth_ai/zyk/lms/structured_outputs/__init__.py +0 -0
  43. synth_ai/zyk/lms/structured_outputs/handler.py +0 -442
  44. synth_ai/zyk/lms/structured_outputs/inject.py +0 -314
  45. synth_ai/zyk/lms/structured_outputs/rehabilitate.py +0 -187
  46. synth_ai/zyk/lms/tools/base.py +0 -104
  47. synth_ai/zyk/lms/vendors/__init__.py +0 -0
  48. synth_ai/zyk/lms/vendors/base.py +0 -31
  49. synth_ai/zyk/lms/vendors/constants.py +0 -22
  50. synth_ai/zyk/lms/vendors/core/__init__.py +0 -0
  51. synth_ai/zyk/lms/vendors/core/anthropic_api.py +0 -413
  52. synth_ai/zyk/lms/vendors/core/gemini_api.py +0 -306
  53. synth_ai/zyk/lms/vendors/core/mistral_api.py +0 -327
  54. synth_ai/zyk/lms/vendors/core/openai_api.py +0 -185
  55. synth_ai/zyk/lms/vendors/local/__init__.py +0 -0
  56. synth_ai/zyk/lms/vendors/local/ollama.py +0 -0
  57. synth_ai/zyk/lms/vendors/openai_standard.py +0 -375
  58. synth_ai/zyk/lms/vendors/retries.py +0 -3
  59. synth_ai/zyk/lms/vendors/supported/__init__.py +0 -0
  60. synth_ai/zyk/lms/vendors/supported/deepseek.py +0 -73
  61. synth_ai/zyk/lms/vendors/supported/groq.py +0 -16
  62. synth_ai/zyk/lms/vendors/supported/ollama.py +0 -14
  63. synth_ai/zyk/lms/vendors/supported/together.py +0 -11
  64. synth_ai-0.1.0.dev38.dist-info/RECORD +0 -67
  65. synth_ai-0.1.0.dev38.dist-info/top_level.txt +0 -4
  66. tests/test_agent.py +0 -538
  67. tests/test_recursive_structured_outputs.py +0 -180
  68. tests/test_structured_outputs.py +0 -100
  69. {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
- )