grasp_agents 0.5.14__py3-none-any.whl → 0.5.15__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.
grasp_agents/cloud_llm.py CHANGED
@@ -64,6 +64,7 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
64
64
  api_provider: APIProvider | None = None
65
65
  llm_settings: SettingsT_co | None = None
66
66
  rate_limiter: LLMRateLimiter | None = None
67
+ client_timeout: float = 60.0
67
68
  max_client_retries: int = 2 # HTTP client retries for network errors
68
69
  max_response_retries: int = (
69
70
  0 # LLM response retries: try to regenerate to pass validation
@@ -71,7 +72,7 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
71
72
  apply_response_schema_via_provider: bool = False
72
73
  apply_tool_call_schema_via_provider: bool = False
73
74
  async_http_client: httpx.AsyncClient | None = None
74
- async_http_client_params: dict[str, Any] | AsyncHTTPClientParams | None = None
75
+ async_http_client_params: AsyncHTTPClientParams | None = None
75
76
 
76
77
  def __post_init__(self) -> None:
77
78
  if self.rate_limiter is not None:
@@ -1,5 +1,3 @@
1
- from typing import Any
2
-
3
1
  import httpx
4
2
  from pydantic import BaseModel, NonNegativeFloat, PositiveInt
5
3
 
@@ -9,13 +7,26 @@ class AsyncHTTPClientParams(BaseModel):
9
7
  max_connections: PositiveInt = 2000
10
8
  max_keepalive_connections: PositiveInt = 500
11
9
  keepalive_expiry: float | None = 5
10
+ # proxy: str | None = None
11
+ # follow_redirects: bool = False
12
+ # trust_env: bool = True
13
+ # auth: AuthTypes | None = (None,)
14
+ # params: QueryParamTypes | None = (None,)
15
+ # headers: HeaderTypes | None = (None,)
16
+ # cookies: CookieTypes | None = (None,)
12
17
 
13
18
 
14
19
  def create_simple_async_httpx_client(
15
- client_params: AsyncHTTPClientParams | dict[str, Any],
20
+ client_params: AsyncHTTPClientParams,
16
21
  ) -> httpx.AsyncClient:
17
- if isinstance(client_params, dict):
18
- client_params = AsyncHTTPClientParams(**client_params)
22
+ extra_params = client_params.model_dump(
23
+ exclude={
24
+ "timeout",
25
+ "max_connections",
26
+ "max_keepalive_connections",
27
+ "keepalive_expiry",
28
+ }
29
+ )
19
30
  return httpx.AsyncClient(
20
31
  timeout=httpx.Timeout(client_params.timeout),
21
32
  limits=httpx.Limits(
@@ -23,4 +34,5 @@ def create_simple_async_httpx_client(
23
34
  max_keepalive_connections=client_params.max_keepalive_connections,
24
35
  keepalive_expiry=client_params.keepalive_expiry,
25
36
  ),
37
+ **extra_params,
26
38
  )
@@ -45,7 +45,6 @@ class LiteLLMSettings(OpenAILLMSettings, total=False):
45
45
  class LiteLLM(CloudLLM[LiteLLMSettings, LiteLLMConverters]):
46
46
  converters: LiteLLMConverters = field(default_factory=LiteLLMConverters)
47
47
 
48
- timeout: float | None = None
49
48
  # Drop unsupported LLM settings
50
49
  drop_params: bool = True
51
50
  additional_drop_params: list[str] | None = None
@@ -63,7 +62,7 @@ class LiteLLM(CloudLLM[LiteLLMSettings, LiteLLMConverters]):
63
62
  self._lite_llm_completion_params.update(
64
63
  {
65
64
  "max_retries": self.max_client_retries,
66
- "timeout": self.timeout,
65
+ "timeout": self.client_timeout,
67
66
  "drop_params": self.drop_params,
68
67
  "additional_drop_params": self.additional_drop_params,
69
68
  "allowed_openai_params": self.allowed_openai_params,
@@ -195,14 +194,15 @@ class LiteLLM(CloudLLM[LiteLLMSettings, LiteLLMConverters]):
195
194
 
196
195
  async for completion_chunk in stream:
197
196
  # Fix tool call indices to be unique within each choice
198
- for n, choice in enumerate(completion_chunk.choices):
199
- for tc in choice.delta.tool_calls or []:
200
- # Tool call ID is not None only when it is a new tool call
201
- if tc.id and tc.index in tc_indices[n]:
202
- tc.index = max(tc_indices[n]) + 1
203
- tc_indices[n].add(tc.index)
204
-
205
- yield completion_chunk
197
+ if completion_chunk is not None:
198
+ for n, choice in enumerate(completion_chunk.choices):
199
+ for tc in choice.delta.tool_calls or []:
200
+ # Tool call ID is not None only when it is a new tool call
201
+ if tc.id and tc.index in tc_indices[n]:
202
+ tc.index = max(tc_indices[n]) + 1
203
+ tc_indices[n].add(tc.index)
204
+
205
+ yield completion_chunk
206
206
 
207
207
  def combine_completion_chunks(
208
208
  self,
@@ -150,6 +150,7 @@ class OpenAILLM(CloudLLM[OpenAILLMSettings, OpenAIConverters]):
150
150
  base_url=_api_provider.get("base_url"),
151
151
  api_key=_api_provider.get("api_key"),
152
152
  max_retries=self.max_client_retries,
153
+ timeout=self.client_timeout,
153
154
  **_async_openai_client_params,
154
155
  )
155
156
 
grasp_agents/utils.py CHANGED
@@ -6,7 +6,7 @@ from collections.abc import AsyncIterator, Coroutine, Mapping
6
6
  from datetime import UTC, datetime
7
7
  from logging import getLogger
8
8
  from pathlib import Path
9
- from typing import Annotated, Any, TypeVar, get_args, get_origin
9
+ from typing import Annotated, Any, NoReturn, TypeVar, get_args, get_origin
10
10
 
11
11
  from pydantic import TypeAdapter
12
12
  from pydantic import ValidationError as PydanticValidationError
@@ -71,12 +71,15 @@ def is_str_type(t: Any) -> bool:
71
71
  )
72
72
 
73
73
 
74
+ T = TypeVar("T")
75
+
76
+
74
77
  def validate_obj_from_json_or_py_string(
75
78
  s: str,
76
- schema: Any,
79
+ schema: type[T],
77
80
  from_substring: bool = False,
78
81
  strip_language_markdown: bool = True,
79
- ) -> Any:
82
+ ) -> T:
80
83
  try:
81
84
  if is_str_type(schema):
82
85
  parsed = s
@@ -94,19 +97,36 @@ def validate_obj_from_json_or_py_string(
94
97
 
95
98
  def validate_tagged_objs_from_json_or_py_string(
96
99
  s: str,
97
- schema_by_xml_tag: Mapping[str, Any],
100
+ schema_by_xml_tag: Mapping[str, type[T]],
98
101
  from_substring: bool = False,
99
102
  strip_language_markdown: bool = True,
100
- ) -> Mapping[str, Any]:
101
- validated_obj_per_tag: dict[str, Any] = {}
102
- _schema: Any = None
103
+ ) -> Mapping[str, T]:
104
+ validated_obj_per_tag: dict[str, T] = {}
105
+ _schema: type[T] | None = None
103
106
  _tag: str | None = None
104
107
 
105
- try:
106
- for _tag, _schema in schema_by_xml_tag.items():
107
- match = re.search(rf"<{_tag}>\s*(.*?)\s*</{_tag}>", s, re.DOTALL)
108
- if not match:
109
- continue
108
+ def _raise(
109
+ tag: str,
110
+ schema: Any,
111
+ *,
112
+ not_found: bool = False,
113
+ base_exc: Exception | None = None,
114
+ ) -> NoReturn:
115
+ if not_found:
116
+ msg = f"Tag <{tag}> not found in string:\n{s}"
117
+ else:
118
+ msg = (
119
+ f"Failed to validate substring within tag <{tag}> against JSON schema:"
120
+ f"\n{s}\nExpected type: {schema}"
121
+ )
122
+ raise JSONSchemaValidationError(s, schema, message=msg) from base_exc
123
+
124
+ for _tag, _schema in schema_by_xml_tag.items():
125
+ match = re.search(rf"<{_tag}>\s*(.*?)\s*</{_tag}>", s, re.DOTALL)
126
+ if match is None:
127
+ continue
128
+ # _raise(_tag, _schema, not_found=True, base_exc=None)
129
+ try:
110
130
  tagged_substring = match.group(1).strip()
111
131
  validated_obj_per_tag[_tag] = validate_obj_from_json_or_py_string(
112
132
  tagged_substring, # type: ignore[assignment]
@@ -114,12 +134,8 @@ def validate_tagged_objs_from_json_or_py_string(
114
134
  from_substring=from_substring,
115
135
  strip_language_markdown=strip_language_markdown,
116
136
  )
117
- except JSONSchemaValidationError as exc:
118
- err_message = (
119
- f"Failed to validate substring within tag <{_tag}> against JSON schema:"
120
- f"\n{s}\nExpected type: {_schema}"
121
- )
122
- raise JSONSchemaValidationError(s, _schema, message=err_message) from exc
137
+ except JSONSchemaValidationError as exc:
138
+ _raise(_tag, _schema, base_exc=exc)
123
139
 
124
140
  return validated_obj_per_tag
125
141
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_agents
3
- Version: 0.5.14
3
+ Version: 0.5.15
4
4
  Summary: Grasp Agents Library
5
5
  License-File: LICENSE.md
6
6
  Requires-Python: <4,>=3.11.4
7
7
  Requires-Dist: dotenv>=0.9.9
8
8
  Requires-Dist: httpx<1,>=0.27.0
9
- Requires-Dist: litellm>=1.74.4
9
+ Requires-Dist: litellm>=1.76.3
10
10
  Requires-Dist: openai<2,>=1.68.2
11
11
  Requires-Dist: pydantic>=2
12
12
  Requires-Dist: pyyaml>=6.0.2
@@ -1,10 +1,10 @@
1
1
  grasp_agents/__init__.py,sha256=0pRU10xjcpuPdCitYtPK_bJVSUZ89FD4Jsmv1DJ_0GY,1121
2
- grasp_agents/cloud_llm.py,sha256=NwKr1XwpJP-px9xZDI1rngHuDmpSbtJ61VynLacbtNo,13237
2
+ grasp_agents/cloud_llm.py,sha256=o_BhwKxpdht4t_8LRyZQOF7v8QJK58GL3YOvkubdqIA,13253
3
3
  grasp_agents/costs_dict.yaml,sha256=2MFNWtkv5W5WSCcv1Cj13B1iQLVv5Ot9pS_KW2Gu2DA,2510
4
4
  grasp_agents/errors.py,sha256=K-22TCM1Klhsej47Rg5eTqnGiGPaXgKOpdOZZ7cPipw,4633
5
5
  grasp_agents/generics_utils.py,sha256=HTX7G8eoylR-zMKz7JDKC-QnDjwFLqMNMjiI8XoGEow,6545
6
6
  grasp_agents/grasp_logging.py,sha256=H1GYhXdQvVkmauFDZ-KDwvVmPQHZUUm9sRqX_ObK2xI,1111
7
- grasp_agents/http_client.py,sha256=Es8NXGDkp4Nem7g24-jW0KFGA9Hp_o2Cv3cOvjup-iU,859
7
+ grasp_agents/http_client.py,sha256=4i82y59XIrNLeJlWjZhZhIb1Or-uV_WFXSVZymnIo7Y,1216
8
8
  grasp_agents/llm.py,sha256=IeV2QpR4AldVP3THzSETEnsaDx3DYz5HM6dkikSpy4o,10684
9
9
  grasp_agents/llm_agent.py,sha256=Ig4YUsxPGvC444ZGl1eDIwfy46gmyygOe7mleEq0ZYM,14090
10
10
  grasp_agents/llm_agent_memory.py,sha256=XmOT2G8RG5AHd0LR3WuK7VbD-KFFfThmJnuZK2iU3Fs,1856
@@ -17,12 +17,12 @@ grasp_agents/prompt_builder.py,sha256=wNPphkW8RL8501jV4Z7ncsN_sxBDR9Ax7eILLHr-OY
17
17
  grasp_agents/run_context.py,sha256=0kWvOKBzQzx6FbdtDVAoeCOmiRGssN6X4n8YPX_oLBY,687
18
18
  grasp_agents/runner.py,sha256=JL2wSKahbPYVd56NRB09cwco43sjhZPI4XYFCZyOXOA,5173
19
19
  grasp_agents/usage_tracker.py,sha256=ZQfVUUpG0C89hyPWT_JgXnjQOxoYmumcQ9t-aCfcMo8,3561
20
- grasp_agents/utils.py,sha256=qKmGBwrQHw1-BgqRLuGTPKGs3J_zbrpk3nxnP1iZBiQ,6152
20
+ grasp_agents/utils.py,sha256=cp3ox2Pxsa_p6VWdPGnrh6sOdxThSKZSFdlDw56X-xQ,6564
21
21
  grasp_agents/litellm/__init__.py,sha256=wD8RZBYokFDfbS9Cs7nO_zKb3w7RIVwEGj7g2D5CJH0,4510
22
22
  grasp_agents/litellm/completion_chunk_converters.py,sha256=J5PPxzoTBqkvKQnCoBxQxJo7Q8Xfl9cbv2GRZox8Cjo,2689
23
23
  grasp_agents/litellm/completion_converters.py,sha256=JQ7XvQwwc-biFqVMcRO61SL5VGs_SkUvAhUz1QD7EmU,2516
24
24
  grasp_agents/litellm/converters.py,sha256=XjePHii578sXP26Fyhnv0XfwJ3cNTp5PraggTsvcBXo,4778
25
- grasp_agents/litellm/lite_llm.py,sha256=IXSplFiOksN-DjxkhZmB0eEGYJGr06IVR_ktE-2MVr8,8341
25
+ grasp_agents/litellm/lite_llm.py,sha256=ESyzdAOnbnvSfh8JvrsxY6ak9GDWG1Efg9HZPIiosCk,8388
26
26
  grasp_agents/litellm/message_converters.py,sha256=PsGLIJEcAeEoluHIh-utEufJ_9WeMYzXkwnR-8jyULQ,2037
27
27
  grasp_agents/openai/__init__.py,sha256=xaRnblUskiLvypIhMe4NRp9dxCG-gNR7dPiugUbPbhE,4717
28
28
  grasp_agents/openai/completion_chunk_converters.py,sha256=3MnMskdlp7ycsggc1ok1XpCHaP4Us2rLYaxImPLw1eI,2573
@@ -30,7 +30,7 @@ grasp_agents/openai/completion_converters.py,sha256=UlDeQSl0AEFUS-QI5e8rrjfmXZoj
30
30
  grasp_agents/openai/content_converters.py,sha256=sMsZhoatuL_8t0IdVaGWIVZLB4nyi1ajD61GewQmeY4,2503
31
31
  grasp_agents/openai/converters.py,sha256=RKOfMbIJmfFQ7ot0RGR6wrdMbR6_L7PB0UZwxwgM88g,4691
32
32
  grasp_agents/openai/message_converters.py,sha256=fhSN81uK51EGbLyM2-f0MvPX_UBrMy7SF3JQPo-dkXg,4686
33
- grasp_agents/openai/openai_llm.py,sha256=DCvoF7MM_OID2UhTkNcBLeEdf8Ej2SQ-6FhgRGe8l1E,9861
33
+ grasp_agents/openai/openai_llm.py,sha256=ZdmkuedTDda1PEO01_o5TXlBcIlAdjN0zik53DJFBmY,9902
34
34
  grasp_agents/openai/tool_converters.py,sha256=rNH5t2Wir9nuy8Ei0jaxNuzDaXGqTLmLz3VyrnJhyn0,1196
35
35
  grasp_agents/processors/base_processor.py,sha256=OpEbOHNsi3RkVDrWy7sN42UB-0vx0L9_hWGjlA07QsM,10972
36
36
  grasp_agents/processors/parallel_processor.py,sha256=BOXRlPaZ-hooz0hHctqiW_5ldR-yDPYjFxuP7fAbZCI,7911
@@ -52,7 +52,7 @@ grasp_agents/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
52
52
  grasp_agents/workflow/looped_workflow.py,sha256=WHp9O3Za2sBVfY_BLOdvPvtY20XsjZQaWSO2-oAFvOY,6806
53
53
  grasp_agents/workflow/sequential_workflow.py,sha256=e3BIWzy_2novmEWNwIteyMbrzvl1-evHrTBE3r3SpU8,3648
54
54
  grasp_agents/workflow/workflow_processor.py,sha256=DwHz70UOTp9dkbtzH9KE5LkGcT1RdHV7Hdiby0Bu9tw,3535
55
- grasp_agents-0.5.14.dist-info/METADATA,sha256=zXIn4AF6QikIjI4DqPjNzzLfCn3gUXft9j1ktMNQu10,6633
56
- grasp_agents-0.5.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
- grasp_agents-0.5.14.dist-info/licenses/LICENSE.md,sha256=-nNNdWqGB8gJ2O-peFQ2Irshv5tW5pHKyTcYkwvH7CE,1201
58
- grasp_agents-0.5.14.dist-info/RECORD,,
55
+ grasp_agents-0.5.15.dist-info/METADATA,sha256=u3p7ts9PyiHypBSiy8XodXA4nquuv8K6iNdIVmetUJ0,6633
56
+ grasp_agents-0.5.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
+ grasp_agents-0.5.15.dist-info/licenses/LICENSE.md,sha256=-nNNdWqGB8gJ2O-peFQ2Irshv5tW5pHKyTcYkwvH7CE,1201
58
+ grasp_agents-0.5.15.dist-info/RECORD,,