grasp_agents 0.3.10__tar.gz → 0.4.0__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.
Files changed (55) hide show
  1. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/PKG-INFO +2 -1
  2. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/README.md +1 -0
  3. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/pyproject.toml +1 -1
  4. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/cloud_llm.py +70 -77
  5. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/comm_processor.py +21 -11
  6. grasp_agents-0.4.0/src/grasp_agents/errors.py +34 -0
  7. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/http_client.py +7 -5
  8. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/llm.py +3 -9
  9. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/llm_agent.py +92 -103
  10. grasp_agents-0.4.0/src/grasp_agents/llm_agent_memory.py +67 -0
  11. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/llm_policy_executor.py +66 -63
  12. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/memory.py +3 -1
  13. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/completion_chunk_converters.py +4 -3
  14. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/openai_llm.py +14 -20
  15. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/tool_converters.py +0 -1
  16. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/packet_pool.py +1 -1
  17. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/printer.py +6 -6
  18. grasp_agents-0.4.0/src/grasp_agents/processor.py +327 -0
  19. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/prompt_builder.py +41 -55
  20. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/run_context.py +1 -5
  21. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/completion_chunk.py +10 -5
  22. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/content.py +2 -2
  23. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/io.py +4 -4
  24. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/message.py +3 -6
  25. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/tool.py +5 -23
  26. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/usage_tracker.py +2 -4
  27. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/utils.py +37 -15
  28. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/workflow/looped_workflow.py +14 -9
  29. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/workflow/sequential_workflow.py +11 -6
  30. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/workflow/workflow_processor.py +30 -13
  31. grasp_agents-0.3.10/src/grasp_agents/llm_agent_memory.py +0 -58
  32. grasp_agents-0.3.10/src/grasp_agents/message_history.py +0 -140
  33. grasp_agents-0.3.10/src/grasp_agents/processor.py +0 -193
  34. grasp_agents-0.3.10/src/grasp_agents/workflow/parallel_processor.py +0 -95
  35. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/.gitignore +0 -0
  36. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/LICENSE.md +0 -0
  37. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/__init__.py +0 -0
  38. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/costs_dict.yaml +0 -0
  39. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/generics_utils.py +0 -0
  40. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/grasp_logging.py +0 -0
  41. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/__init__.py +0 -0
  42. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/completion_converters.py +0 -0
  43. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/content_converters.py +0 -0
  44. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/converters.py +0 -0
  45. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/openai/message_converters.py +0 -0
  46. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/packet.py +0 -0
  47. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/rate_limiting/__init__.py +0 -0
  48. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/rate_limiting/rate_limiter_chunked.py +0 -0
  49. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/rate_limiting/types.py +0 -0
  50. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/rate_limiting/utils.py +0 -0
  51. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/__init__.py +0 -0
  52. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/completion.py +0 -0
  53. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/converters.py +0 -0
  54. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/typing/events.py +0 -0
  55. {grasp_agents-0.3.10 → grasp_agents-0.4.0}/src/grasp_agents/workflow/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_agents
3
- Version: 0.3.10
3
+ Version: 0.4.0
4
4
  Summary: Grasp Agents Library
5
5
  License-File: LICENSE.md
6
6
  Requires-Python: <4,>=3.11.4
@@ -139,6 +139,7 @@ You should first ask the student about their education, interests, and preferenc
139
139
  * Provide your thinking before asking a question and after receiving a reply.
140
140
  * Do not include your exact question as part of your thinking.
141
141
  * The problem must have all the necessary data.
142
+ * Use the final answer tool to provide the problem.
142
143
  """
143
144
 
144
145
  # Tool input must be a Pydantic model to infer the JSON schema used by the LLM APIs
@@ -123,6 +123,7 @@ You should first ask the student about their education, interests, and preferenc
123
123
  * Provide your thinking before asking a question and after receiving a reply.
124
124
  * Do not include your exact question as part of your thinking.
125
125
  * The problem must have all the necessary data.
126
+ * Use the final answer tool to provide the problem.
126
127
  """
127
128
 
128
129
  # Tool input must be a Pydantic model to infer the JSON schema used by the LLM APIs
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "grasp_agents"
3
- version = "0.3.10"
3
+ version = "0.4.0"
4
4
  description = "Grasp Agents Library"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11.4,<4"
@@ -2,9 +2,9 @@ import fnmatch
2
2
  import logging
3
3
  import os
4
4
  from abc import abstractmethod
5
- from collections.abc import AsyncIterator, Mapping, Sequence
5
+ from collections.abc import AsyncIterator, Mapping
6
6
  from copy import deepcopy
7
- from typing import Any, Generic, Literal
7
+ from typing import Any, Generic, Literal, NotRequired
8
8
 
9
9
  import httpx
10
10
  from pydantic import BaseModel
@@ -16,12 +16,15 @@ from tenacity import (
16
16
  )
17
17
  from typing_extensions import TypedDict
18
18
 
19
- from .http_client import AsyncHTTPClientParams, create_async_http_client
19
+ from .http_client import AsyncHTTPClientParams, create_simple_async_httpx_client
20
20
  from .llm import LLM, ConvertT_co, LLMSettings, SettingsT_co
21
- from .message_history import MessageHistory
22
- from .rate_limiting.rate_limiter_chunked import RateLimiterC, limit_rate_chunked
21
+ from .rate_limiting.rate_limiter_chunked import RateLimiterC, limit_rate
23
22
  from .typing.completion import Completion
24
- from .typing.completion_chunk import CompletionChunk, combine_completion_chunks
23
+ from .typing.completion_chunk import (
24
+ CompletionChoice,
25
+ CompletionChunk,
26
+ combine_completion_chunks,
27
+ )
25
28
  from .typing.events import CompletionChunkEvent, CompletionEvent
26
29
  from .typing.message import AssistantMessage, Messages
27
30
  from .typing.tool import BaseTool, ToolChoice
@@ -29,30 +32,30 @@ from .typing.tool import BaseTool, ToolChoice
29
32
  logger = logging.getLogger(__name__)
30
33
 
31
34
 
32
- APIProvider = Literal["openai", "openrouter", "google_ai_studio"]
35
+ APIProviderName = Literal["openai", "openrouter", "google_ai_studio"]
33
36
 
34
37
 
35
- class APIProviderInfo(TypedDict):
36
- name: APIProvider
38
+ class APIProvider(TypedDict):
39
+ name: APIProviderName
37
40
  base_url: str
38
- api_key: str | None
39
- struct_outputs_support: tuple[str, ...]
41
+ api_key: NotRequired[str | None]
42
+ struct_outputs_support: NotRequired[tuple[str, ...]]
40
43
 
41
44
 
42
- PROVIDERS: dict[APIProvider, APIProviderInfo] = {
43
- "openai": APIProviderInfo(
45
+ API_PROVIDERS: dict[APIProviderName, APIProvider] = {
46
+ "openai": APIProvider(
44
47
  name="openai",
45
48
  base_url="https://api.openai.com/v1",
46
49
  api_key=os.getenv("OPENAI_API_KEY"),
47
50
  struct_outputs_support=("*",),
48
51
  ),
49
- "openrouter": APIProviderInfo(
52
+ "openrouter": APIProvider(
50
53
  name="openrouter",
51
54
  base_url="https://openrouter.ai/api/v1",
52
55
  api_key=os.getenv("OPENROUTER_API_KEY"),
53
56
  struct_outputs_support=(),
54
57
  ),
55
- "google_ai_studio": APIProviderInfo(
58
+ "google_ai_studio": APIProvider(
56
59
  name="google_ai_studio",
57
60
  base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
58
61
  api_key=os.getenv("GOOGLE_AI_STUDIO_API_KEY"),
@@ -61,27 +64,33 @@ PROVIDERS: dict[APIProvider, APIProviderInfo] = {
61
64
  }
62
65
 
63
66
 
64
- def retry_error_callback(retry_state: RetryCallState) -> None:
65
- assert retry_state.outcome is not None
66
- exception = retry_state.outcome.exception()
67
+ def retry_error_callback(retry_state: RetryCallState) -> Completion:
68
+ exception = retry_state.outcome.exception() if retry_state.outcome else None
67
69
  if exception:
68
70
  if retry_state.attempt_number == 1:
69
- logger.error(
70
- f"CloudLLM completion request failed:\n{exception}",
71
- exc_info=exception,
71
+ logger.warning(
72
+ f"\nCloudLLM completion request failed:\n{exception}",
73
+ # exc_info=exception,
72
74
  )
73
75
  if retry_state.attempt_number > 1:
74
76
  logger.warning(
75
- f"CloudLLM completion request failed after retrying:\n{exception}",
76
- exc_info=exception,
77
+ f"\nCloudLLM completion request failed after retrying:\n{exception}",
78
+ # exc_info=exception,
77
79
  )
80
+ failed_message = AssistantMessage(content=None, refusal=str(exception))
81
+
82
+ return Completion(
83
+ model="",
84
+ choices=[CompletionChoice(message=failed_message, finish_reason=None, index=0)],
85
+ )
78
86
 
79
87
 
80
- def retry_before_callback(retry_state: RetryCallState) -> None:
81
- if retry_state.attempt_number > 1:
88
+ def retry_before_sleep_callback(retry_state: RetryCallState) -> None:
89
+ exception = retry_state.outcome.exception() if retry_state.outcome else None
90
+ if exception:
82
91
  logger.info(
83
- "Retrying CloudLLM completion request "
84
- f"(attempt {retry_state.attempt_number - 1}) ..."
92
+ "\nRetrying CloudLLM completion request "
93
+ f"(attempt {retry_state.attempt_number}):\n{exception}"
85
94
  )
86
95
 
87
96
 
@@ -96,10 +105,13 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
96
105
  model_name: str,
97
106
  converters: ConvertT_co,
98
107
  llm_settings: SettingsT_co | None = None,
99
- model_id: str | None = None,
100
108
  tools: list[BaseTool[BaseModel, Any, Any]] | None = None,
101
109
  response_format: type | Mapping[str, type] | None = None,
110
+ model_id: str | None = None,
111
+ # Custom LLM provider
112
+ api_provider: APIProvider | None = None,
102
113
  # Connection settings
114
+ async_http_client: httpx.AsyncClient | None = None,
103
115
  async_http_client_params: (
104
116
  dict[str, Any] | AsyncHTTPClientParams | None
105
117
  ) = None,
@@ -110,8 +122,6 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
110
122
  rate_limiter_max_concurrency: int = 300,
111
123
  # Retries
112
124
  num_generation_retries: int = 0,
113
- # Disable tqdm for batch processing
114
- no_tqdm: bool = True,
115
125
  **kwargs: Any,
116
126
  ) -> None:
117
127
  self.llm_settings: CloudLLMSettings | None
@@ -129,29 +139,31 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
129
139
  self._model_name = model_name
130
140
  model_name_parts = model_name.split(":", 1)
131
141
 
132
- if len(model_name_parts) == 2 and model_name_parts[0] in PROVIDERS:
133
- api_provider, api_model_name = model_name_parts
134
- if api_provider not in PROVIDERS:
142
+ if len(model_name_parts) == 2:
143
+ api_provider_name, api_model_name = model_name_parts
144
+ self._api_model_name: str = api_model_name
145
+ if api_provider_name not in API_PROVIDERS:
135
146
  raise ValueError(
136
- f"API provider '{api_provider}' is not supported. "
137
- f"Supported providers are: {', '.join(PROVIDERS.keys())}"
147
+ f"API provider '{api_provider_name}' is not supported. "
148
+ f"Supported providers are: {', '.join(API_PROVIDERS.keys())}"
138
149
  )
139
-
140
- self._api_provider: APIProvider | None = api_provider
141
- self._api_model_name: str = api_model_name
142
- self._base_url: str | None = PROVIDERS[api_provider]["base_url"]
143
- self._api_key: str | None = PROVIDERS[api_provider]["api_key"]
144
- self._struct_outputs_support: bool = any(
145
- fnmatch.fnmatch(self._model_name, pat)
146
- for pat in PROVIDERS[api_provider]["struct_outputs_support"]
150
+ _api_provider = API_PROVIDERS[api_provider_name]
151
+ elif api_provider is not None:
152
+ self._api_model_name: str = model_name
153
+ _api_provider = api_provider
154
+ else:
155
+ raise ValueError(
156
+ "API provider must be specified either in the model name "
157
+ "or as a separate argument."
147
158
  )
148
159
 
149
- else:
150
- self._api_provider = None
151
- self._api_model_name = model_name
152
- self._base_url = None
153
- self._api_key = None
154
- self._struct_outputs_support = False
160
+ self._api_provider_name: APIProviderName = _api_provider["name"]
161
+ self._base_url: str | None = _api_provider.get("base_url")
162
+ self._api_key: str | None = _api_provider.get("api_key")
163
+ self._struct_outputs_support: bool = any(
164
+ fnmatch.fnmatch(self._model_name, pat)
165
+ for pat in _api_provider.get("struct_outputs_support", ())
166
+ )
155
167
 
156
168
  if (
157
169
  self._llm_settings.get("use_struct_outputs")
@@ -171,23 +183,20 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
171
183
  max_concurrency=rate_limiter_max_concurrency,
172
184
  )
173
185
  )
174
- self.no_tqdm = no_tqdm
175
- self._client: Any
176
186
 
177
187
  self._async_http_client: httpx.AsyncClient | None = None
178
- if async_http_client_params is not None:
179
- val_async_http_client_params = AsyncHTTPClientParams.model_validate(
188
+ if async_http_client is not None:
189
+ self._async_http_client = async_http_client
190
+ elif async_http_client_params is not None:
191
+ self._async_http_client = create_simple_async_httpx_client(
180
192
  async_http_client_params
181
193
  )
182
- self._async_http_client = create_async_http_client(
183
- val_async_http_client_params
184
- )
185
194
 
186
195
  self.num_generation_retries = num_generation_retries
187
196
 
188
197
  @property
189
- def api_provider(self) -> APIProvider | None:
190
- return self._api_provider
198
+ def api_provider_name(self) -> APIProviderName | None:
199
+ return self._api_provider_name
191
200
 
192
201
  @property
193
202
  def rate_limiter(
@@ -343,7 +352,8 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
343
352
 
344
353
  return iterate()
345
354
 
346
- async def generate_completion(
355
+ @limit_rate
356
+ async def generate_completion( # type: ignore[override]
347
357
  self,
348
358
  conversation: Messages,
349
359
  *,
@@ -353,7 +363,7 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
353
363
  wrapped_func = retry(
354
364
  wait=wait_random_exponential(min=1, max=8),
355
365
  stop=stop_after_attempt(self.num_generation_retries + 1),
356
- before=retry_before_callback,
366
+ before_sleep=retry_before_sleep_callback,
357
367
  retry_error_callback=retry_error_callback,
358
368
  )(self.__class__.generate_completion_no_retry)
359
369
 
@@ -361,23 +371,6 @@ class CloudLLM(LLM[SettingsT_co, ConvertT_co], Generic[SettingsT_co, ConvertT_co
361
371
  self, conversation, tool_choice=tool_choice, n_choices=n_choices
362
372
  )
363
373
 
364
- @limit_rate_chunked # type: ignore
365
- async def _generate_completion_batch(
366
- self,
367
- conversation: Messages,
368
- *,
369
- tool_choice: ToolChoice | None = None,
370
- ) -> Completion:
371
- return await self.generate_completion(conversation, tool_choice=tool_choice)
372
-
373
- async def generate_completion_batch(
374
- self, message_history: MessageHistory, *, tool_choice: ToolChoice | None = None
375
- ) -> Sequence[Completion]:
376
- return await self._generate_completion_batch(
377
- list(message_history.conversations), # type: ignore
378
- tool_choice=tool_choice,
379
- )
380
-
381
374
  def _get_rate_limiter(
382
375
  self,
383
376
  rate_limiter: RateLimiterC[Messages, AssistantMessage] | None = None,
@@ -5,12 +5,13 @@ from typing import Any, ClassVar, Generic, Protocol, TypeVar, cast
5
5
  from pydantic import BaseModel
6
6
  from pydantic.json_schema import SkipJsonSchema
7
7
 
8
+ from .memory import MemT
8
9
  from .packet import Packet
9
10
  from .packet_pool import PacketPool
10
11
  from .processor import Processor
11
12
  from .run_context import CtxT, RunContext
12
13
  from .typing.events import Event, PacketEvent
13
- from .typing.io import InT_contra, MemT_co, OutT_co, ProcName
14
+ from .typing.io import InT, OutT_co, ProcName
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -31,8 +32,8 @@ class ExitCommunicationHandler(Protocol[_OutT_contra, CtxT]):
31
32
 
32
33
 
33
34
  class CommProcessor(
34
- Processor[InT_contra, OutT_co, MemT_co, CtxT],
35
- Generic[InT_contra, OutT_co, MemT_co, CtxT],
35
+ Processor[InT, OutT_co, MemT, CtxT],
36
+ Generic[InT, OutT_co, MemT, CtxT],
36
37
  ):
37
38
  _generic_arg_to_instance_attr_map: ClassVar[dict[int, str]] = {
38
39
  0: "_in_type",
@@ -45,8 +46,9 @@ class CommProcessor(
45
46
  *,
46
47
  recipients: Sequence[ProcName] | None = None,
47
48
  packet_pool: PacketPool[CtxT] | None = None,
49
+ num_par_run_retries: int = 0,
48
50
  ) -> None:
49
- super().__init__(name=name)
51
+ super().__init__(name=name, num_par_run_retries=num_par_run_retries)
50
52
 
51
53
  self.recipients = recipients or []
52
54
 
@@ -56,6 +58,10 @@ class CommProcessor(
56
58
  ExitCommunicationHandler[OutT_co, CtxT] | None
57
59
  ) = None
58
60
 
61
+ @property
62
+ def packet_pool(self) -> PacketPool[CtxT] | None:
63
+ return self._packet_pool
64
+
59
65
  def _validate_routing(self, payloads: Sequence[OutT_co]) -> Sequence[ProcName]:
60
66
  if all(isinstance(p, DynCommPayload) for p in payloads):
61
67
  payloads_ = cast("Sequence[DynCommPayload]", payloads)
@@ -88,9 +94,10 @@ class CommProcessor(
88
94
  self,
89
95
  chat_inputs: Any | None = None,
90
96
  *,
91
- in_packet: Packet[InT_contra] | None = None,
92
- in_args: InT_contra | Sequence[InT_contra] | None = None,
97
+ in_packet: Packet[InT] | None = None,
98
+ in_args: InT | Sequence[InT] | None = None,
93
99
  forgetful: bool = False,
100
+ run_id: str | None = None,
94
101
  ctx: RunContext[CtxT] | None = None,
95
102
  ) -> Packet[OutT_co]:
96
103
  out_packet = await super().run(
@@ -98,6 +105,7 @@ class CommProcessor(
98
105
  in_packet=in_packet,
99
106
  in_args=in_args,
100
107
  forgetful=forgetful,
108
+ run_id=run_id,
101
109
  ctx=ctx,
102
110
  )
103
111
  recipients = self._validate_routing(out_packet.payloads)
@@ -114,9 +122,10 @@ class CommProcessor(
114
122
  self,
115
123
  chat_inputs: Any | None = None,
116
124
  *,
117
- in_packet: Packet[InT_contra] | None = None,
118
- in_args: InT_contra | Sequence[InT_contra] | None = None,
125
+ in_packet: Packet[InT] | None = None,
126
+ in_args: InT | None = None,
119
127
  forgetful: bool = False,
128
+ run_id: str | None = None,
120
129
  ctx: RunContext[CtxT] | None = None,
121
130
  ) -> AsyncIterator[Event[Any]]:
122
131
  out_packet: Packet[OutT_co] | None = None
@@ -125,6 +134,7 @@ class CommProcessor(
125
134
  in_packet=in_packet,
126
135
  in_args=in_args,
127
136
  forgetful=forgetful,
137
+ run_id=run_id,
128
138
  ctx=ctx,
129
139
  ):
130
140
  if isinstance(event, PacketEvent):
@@ -152,7 +162,7 @@ class CommProcessor(
152
162
 
153
163
  return func
154
164
 
155
- def _exit_communication_fn(
165
+ def _exit_communication(
156
166
  self, out_packet: Packet[OutT_co], ctx: RunContext[CtxT] | None
157
167
  ) -> bool:
158
168
  if self._exit_communication_impl:
@@ -162,7 +172,7 @@ class CommProcessor(
162
172
 
163
173
  async def _packet_handler(
164
174
  self,
165
- packet: Packet[InT_contra],
175
+ packet: Packet[InT],
166
176
  ctx: RunContext[CtxT] | None = None,
167
177
  **run_kwargs: Any,
168
178
  ) -> None:
@@ -170,7 +180,7 @@ class CommProcessor(
170
180
 
171
181
  out_packet = await self.run(ctx=ctx, in_packet=packet, **run_kwargs)
172
182
 
173
- if self._exit_communication_fn(out_packet=out_packet, ctx=ctx):
183
+ if self._exit_communication(out_packet=out_packet, ctx=ctx):
174
184
  await self._packet_pool.stop_all()
175
185
  return
176
186
 
@@ -0,0 +1,34 @@
1
+ class InputValidationError(Exception):
2
+ pass
3
+
4
+
5
+ class StringParsingError(Exception):
6
+ pass
7
+
8
+
9
+ class CompletionError(Exception):
10
+ pass
11
+
12
+
13
+ class CombineCompletionChunksError(Exception):
14
+ pass
15
+
16
+
17
+ class ToolValidationError(Exception):
18
+ pass
19
+
20
+
21
+ class OutputValidationError(Exception):
22
+ pass
23
+
24
+
25
+ class WorkflowConstructionError(Exception):
26
+ pass
27
+
28
+
29
+ class SystemPromptBuilderError(Exception):
30
+ pass
31
+
32
+
33
+ class InputPromptBuilderError(Exception):
34
+ pass
@@ -1,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  import httpx
2
4
  from pydantic import BaseModel, NonNegativeFloat, PositiveInt
3
5
 
@@ -9,10 +11,12 @@ class AsyncHTTPClientParams(BaseModel):
9
11
  keepalive_expiry: float | None = 5
10
12
 
11
13
 
12
- def create_async_http_client(
13
- client_params: AsyncHTTPClientParams,
14
+ def create_simple_async_httpx_client(
15
+ client_params: AsyncHTTPClientParams | dict[str, Any],
14
16
  ) -> httpx.AsyncClient:
15
- http_client = httpx.AsyncClient(
17
+ if isinstance(client_params, dict):
18
+ client_params = AsyncHTTPClientParams(**client_params)
19
+ return httpx.AsyncClient(
16
20
  timeout=httpx.Timeout(client_params.timeout),
17
21
  limits=httpx.Limits(
18
22
  max_connections=client_params.max_connections,
@@ -20,5 +24,3 @@ def create_async_http_client(
20
24
  keepalive_expiry=client_params.keepalive_expiry,
21
25
  ),
22
26
  )
23
-
24
- return http_client
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from abc import ABC, abstractmethod
3
- from collections.abc import AsyncIterator, Mapping, Sequence
3
+ from collections.abc import AsyncIterator, Mapping
4
4
  from typing import Any, Generic, TypeVar, cast
5
5
  from uuid import uuid4
6
6
 
@@ -9,7 +9,7 @@ from typing_extensions import TypedDict
9
9
 
10
10
  from grasp_agents.utils import validate_obj_from_json_or_py_string
11
11
 
12
- from .message_history import MessageHistory
12
+ from .errors import ToolValidationError
13
13
  from .typing.completion import Completion
14
14
  from .typing.converters import Converters
15
15
  from .typing.events import CompletionChunkEvent, CompletionEvent
@@ -118,7 +118,7 @@ class LLM(ABC, Generic[SettingsT_co, ConvertT_co]):
118
118
 
119
119
  available_tool_names = list(self.tools) if self.tools else []
120
120
  if tool_name not in available_tool_names or not self.tools:
121
- raise ValueError(
121
+ raise ToolValidationError(
122
122
  f"Tool '{tool_name}' is not available in the LLM tools "
123
123
  f"(available: {available_tool_names}"
124
124
  )
@@ -146,9 +146,3 @@ class LLM(ABC, Generic[SettingsT_co, ConvertT_co]):
146
146
  n_choices: int | None = None,
147
147
  ) -> AsyncIterator[CompletionChunkEvent | CompletionEvent]:
148
148
  pass
149
-
150
- @abstractmethod
151
- async def generate_completion_batch(
152
- self, message_history: MessageHistory, *, tool_choice: ToolChoice | None = None
153
- ) -> Sequence[Completion]:
154
- pass