grasp_agents 0.3.11__py3-none-any.whl → 0.4.2__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.
@@ -42,12 +42,10 @@ class UserMessage(MessageBase):
42
42
  def from_formatted_prompt(
43
43
  cls,
44
44
  prompt_template: str,
45
- prompt_args: Mapping[str, str | int | bool | ImageData] | None = None,
46
45
  name: str | None = None,
46
+ prompt_args: Mapping[str, str | int | bool | ImageData] | None = None,
47
47
  ) -> "UserMessage":
48
- content = Content.from_formatted_prompt(
49
- prompt_template=prompt_template, prompt_args=prompt_args
50
- )
48
+ content = Content.from_formatted_prompt(prompt_template, **(prompt_args or {}))
51
49
 
52
50
  return cls(content=content, name=name)
53
51
 
@@ -74,10 +72,7 @@ class ToolMessage(MessageBase):
74
72
 
75
73
  @classmethod
76
74
  def from_tool_output(
77
- cls,
78
- tool_output: Any,
79
- tool_call: ToolCall,
80
- indent: int = 2,
75
+ cls, tool_output: Any, tool_call: ToolCall, indent: int = 2
81
76
  ) -> "ToolMessage":
82
77
  return cls(
83
78
  content=json.dumps(tool_output, default=pydantic_encoder, indent=indent),
@@ -24,7 +24,7 @@ else:
24
24
  """Runtime placeholder so RunContext[CtxT] works"""
25
25
 
26
26
 
27
- _InT_contra = TypeVar("_InT_contra", bound=BaseModel, contravariant=True)
27
+ _InT = TypeVar("_InT", bound=BaseModel)
28
28
  _OutT_co = TypeVar("_OutT_co", covariant=True)
29
29
 
30
30
 
@@ -38,7 +38,7 @@ class BaseTool(
38
38
  AutoInstanceAttributesMixin,
39
39
  BaseModel,
40
40
  ABC,
41
- Generic[_InT_contra, _OutT_co, CtxT],
41
+ Generic[_InT, _OutT_co, CtxT],
42
42
  ):
43
43
  _generic_arg_to_instance_attr_map: ClassVar[dict[int, str]] = {
44
44
  0: "_in_type",
@@ -48,37 +48,19 @@ class BaseTool(
48
48
  name: str
49
49
  description: str
50
50
 
51
- _in_type: type[_InT_contra] = PrivateAttr()
51
+ _in_type: type[_InT] = PrivateAttr()
52
52
  _out_type: type[_OutT_co] = PrivateAttr()
53
53
 
54
- # _in_type_adapter: TypeAdapter[_InT_contra] = PrivateAttr()
55
- # _out_type_adapter: TypeAdapter[_OutT_co] = PrivateAttr()
56
-
57
- # def model_post_init(self, context: Any) -> None:
58
- # self._in_type_adapter = TypeAdapter(self._in_type)
59
- # self._out_type_adapter = TypeAdapter(self._out_type)
60
-
61
54
  @property
62
- def in_type(self) -> type[_InT_contra]: # type: ignore[reportInvalidTypeVarUse]
63
- # Exposing the type of a contravariant variable only, should be type safe
55
+ def in_type(self) -> type[_InT]:
64
56
  return self._in_type
65
57
 
66
58
  @property
67
59
  def out_type(self) -> type[_OutT_co]:
68
60
  return self._out_type
69
61
 
70
- # @property
71
- # def in_type_adapter(self) -> TypeAdapter[_InT_contra]:
72
- # return self._in_type_adapter
73
-
74
- # @property
75
- # def out_type_adapter(self) -> TypeAdapter[_OutT_co]:
76
- # return self._out_type_adapter
77
-
78
62
  @abstractmethod
79
- async def run(
80
- self, inp: _InT_contra, ctx: RunContext[CtxT] | None = None
81
- ) -> _OutT_co:
63
+ async def run(self, inp: _InT, ctx: RunContext[CtxT] | None = None) -> _OutT_co:
82
64
  pass
83
65
 
84
66
  async def __call__(
@@ -21,7 +21,6 @@ CostsDict: TypeAlias = dict[str, ModelCostsDict]
21
21
 
22
22
  class UsageTracker(BaseModel):
23
23
  # TODO: specify different costs per provider:model, not just per model
24
- source_id: str
25
24
  costs_dict_path: str | Path = COSTS_DICT_PATH
26
25
  costs_dict: CostsDict | None = None
27
26
  usages: dict[str, Usage] = Field(default_factory=dict)
@@ -92,8 +91,7 @@ class UsageTracker(BaseModel):
92
91
  logger.debug("\n-------------------")
93
92
 
94
93
  token_usage_str = (
95
- f"Total {self.source_id} I/O/(R)/(C) tokens: "
96
- f"{usage.input_tokens}/{usage.output_tokens}"
94
+ f"Total I/O/(R)/(C) tokens: {usage.input_tokens}/{usage.output_tokens}"
97
95
  )
98
96
  if usage.reasoning_tokens is not None:
99
97
  token_usage_str += f"/{usage.reasoning_tokens}"
@@ -104,7 +102,7 @@ class UsageTracker(BaseModel):
104
102
  if usage.cost is not None:
105
103
  logger.debug(
106
104
  colored(
107
- f"Total {self.source_id} cost: ${usage.cost:.4f}",
105
+ f"Total cost: ${usage.cost:.4f}",
108
106
  "light_grey",
109
107
  )
110
108
  )
grasp_agents/utils.py CHANGED
@@ -6,11 +6,13 @@ from collections.abc import 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 Any, TypeVar, get_args, overload
9
+ from typing import Annotated, Any, TypeVar, get_args, get_origin, overload
10
10
 
11
11
  from pydantic import TypeAdapter, ValidationError
12
12
  from tqdm.autonotebook import tqdm
13
13
 
14
+ from .errors import OutputValidationError, StringParsingError
15
+
14
16
  logger = getLogger(__name__)
15
17
 
16
18
  _JSON_START_RE = re.compile(r"[{\[]")
@@ -25,35 +27,41 @@ def extract_json_substring(text: str) -> str | None:
25
27
  try:
26
28
  _, end = decoder.raw_decode(text, idx=start)
27
29
  return text[start:end]
28
- except ValueError:
30
+ except json.JSONDecodeError:
29
31
  continue
30
32
 
31
33
  return None
32
34
 
33
35
 
34
36
  def parse_json_or_py_string(
35
- s: str, return_none_on_failure: bool = False
37
+ s: str, return_none_on_failure: bool = False, strip_language_markdown: bool = True
36
38
  ) -> dict[str, Any] | list[Any] | None:
37
- s_fmt = re.sub(r"```[a-zA-Z0-9]*\n|```", "", s).strip()
39
+ s_orig = s
40
+ if strip_language_markdown:
41
+ s = re.sub(r"```[a-zA-Z0-9]*\n|```", "", s).strip()
38
42
  try:
39
- return ast.literal_eval(s_fmt)
43
+ return ast.literal_eval(s)
40
44
  except (ValueError, SyntaxError):
41
45
  try:
42
- return json.loads(s_fmt)
46
+ return json.loads(s)
43
47
  except json.JSONDecodeError as exc:
44
48
  if return_none_on_failure:
45
49
  return None
46
- raise ValueError(
50
+ raise StringParsingError(
47
51
  "Invalid JSON/Python string - Both ast.literal_eval and json.loads "
48
- f"failed to parse the following response:\n{s}"
52
+ f"failed to parse the following response:\n{s_orig}"
49
53
  ) from exc
50
54
 
51
55
 
52
56
  def parse_json_or_py_substring(
53
- json_str: str, return_none_on_failure: bool = False
57
+ json_str: str,
58
+ return_none_on_failure: bool = False,
59
+ strip_language_markdown: bool = True,
54
60
  ) -> dict[str, Any] | list[Any] | None:
55
61
  return parse_json_or_py_string(
56
- extract_json_substring(json_str) or "", return_none_on_failure
62
+ extract_json_substring(json_str) or "",
63
+ return_none_on_failure=return_none_on_failure,
64
+ strip_language_markdown=strip_language_markdown,
57
65
  )
58
66
 
59
67
 
@@ -62,6 +70,7 @@ def validate_obj_from_json_or_py_string(
62
70
  s: str,
63
71
  adapter: TypeAdapter[T],
64
72
  from_substring: bool = False,
73
+ strip_language_markdown: bool = True,
65
74
  ) -> T: ...
66
75
 
67
76
 
@@ -70,6 +79,7 @@ def validate_obj_from_json_or_py_string(
70
79
  s: str,
71
80
  adapter: Mapping[str, TypeAdapter[T]],
72
81
  from_substring: bool = False,
82
+ strip_language_markdown: bool = True,
73
83
  ) -> T | str: ...
74
84
 
75
85
 
@@ -77,6 +87,7 @@ def validate_obj_from_json_or_py_string(
77
87
  s: str,
78
88
  adapter: TypeAdapter[T] | Mapping[str, TypeAdapter[T]],
79
89
  from_substring: bool = False,
90
+ strip_language_markdown: bool = True,
80
91
  ) -> T | str:
81
92
  _selected_adapter: TypeAdapter[T] | None = None
82
93
  if isinstance(adapter, Mapping):
@@ -89,22 +100,33 @@ def validate_obj_from_json_or_py_string(
89
100
  _selected_adapter = adapter
90
101
 
91
102
  _type = _selected_adapter._type # type: ignore[attr-defined]
103
+ type_origin = get_origin(_type)
92
104
  type_args = get_args(_type)
93
- is_str_type = (_type is str) or (len(type_args) == 1 and type_args[0] is str)
105
+ is_str_type = (_type is str) or (
106
+ type_origin is Annotated and type_args and type_args[0] is str
107
+ )
94
108
 
95
109
  try:
96
110
  if not is_str_type:
97
111
  if from_substring:
98
- parsed = parse_json_or_py_substring(s, return_none_on_failure=True)
112
+ parsed = parse_json_or_py_substring(
113
+ s,
114
+ return_none_on_failure=True,
115
+ strip_language_markdown=strip_language_markdown,
116
+ )
99
117
  else:
100
- parsed = parse_json_or_py_string(s, return_none_on_failure=True)
118
+ parsed = parse_json_or_py_string(
119
+ s,
120
+ return_none_on_failure=True,
121
+ strip_language_markdown=strip_language_markdown,
122
+ )
101
123
  if parsed is None:
102
124
  parsed = s
103
125
  else:
104
126
  parsed = s
105
127
  return _selected_adapter.validate_python(parsed)
106
- except (json.JSONDecodeError, ValidationError) as exc:
107
- raise ValueError(
128
+ except ValidationError as exc:
129
+ raise OutputValidationError(
108
130
  f"Invalid JSON or Python string:\n{s}\nExpected type: {_type}"
109
131
  ) from exc
110
132
 
@@ -3,10 +3,11 @@ from itertools import pairwise
3
3
  from logging import getLogger
4
4
  from typing import Any, ClassVar, Generic, Protocol, TypeVar, cast, final
5
5
 
6
+ from ..errors import WorkflowConstructionError
6
7
  from ..packet_pool import Packet, PacketPool
7
8
  from ..processor import Processor
8
9
  from ..run_context import CtxT, RunContext
9
- from ..typing.io import InT_contra, OutT_co, ProcName
10
+ from ..typing.io import InT, OutT_co, ProcName
10
11
  from .workflow_processor import WorkflowProcessor
11
12
 
12
13
  logger = getLogger(__name__)
@@ -24,7 +25,7 @@ class ExitWorkflowLoopHandler(Protocol[_OutT_contra, CtxT]):
24
25
 
25
26
 
26
27
  class LoopedWorkflow(
27
- WorkflowProcessor[InT_contra, OutT_co, CtxT], Generic[InT_contra, OutT_co, CtxT]
28
+ WorkflowProcessor[InT, OutT_co, CtxT], Generic[InT, OutT_co, CtxT]
28
29
  ):
29
30
  _generic_arg_to_instance_attr_map: ClassVar[dict[int, str]] = {
30
31
  0: "_in_type",
@@ -38,6 +39,7 @@ class LoopedWorkflow(
38
39
  exit_proc: Processor[Any, OutT_co, Any, CtxT],
39
40
  packet_pool: PacketPool[CtxT] | None = None,
40
41
  recipients: list[ProcName] | None = None,
42
+ num_par_run_retries: int = 0,
41
43
  max_iterations: int = 10,
42
44
  ) -> None:
43
45
  super().__init__(
@@ -47,17 +49,18 @@ class LoopedWorkflow(
47
49
  end_proc=exit_proc,
48
50
  packet_pool=packet_pool,
49
51
  recipients=recipients,
52
+ num_par_run_retries=num_par_run_retries,
50
53
  )
51
54
 
52
55
  for prev_proc, proc in pairwise(subprocs):
53
56
  if prev_proc.out_type != proc.in_type:
54
- raise ValueError(
57
+ raise WorkflowConstructionError(
55
58
  f"Output type {prev_proc.out_type} of subprocessor "
56
59
  f"{prev_proc.name} does not match input type {proc.in_type} of "
57
60
  f"subprocessor {proc.name}"
58
61
  )
59
62
  if subprocs[-1].out_type != subprocs[0].in_type:
60
- raise ValueError(
63
+ raise WorkflowConstructionError(
61
64
  "Looped workflow's last subprocessor output type "
62
65
  f"{subprocs[-1].out_type} does not match first subprocessor input "
63
66
  f"type {subprocs[0].in_type}"
@@ -80,7 +83,7 @@ class LoopedWorkflow(
80
83
 
81
84
  return func
82
85
 
83
- def _exit_workflow_loop_fn(
86
+ def _exit_workflow_loop(
84
87
  self,
85
88
  out_packet: Packet[OutT_co],
86
89
  *,
@@ -97,10 +100,11 @@ class LoopedWorkflow(
97
100
  self,
98
101
  chat_inputs: Any | None = None,
99
102
  *,
100
- in_packet: Packet[InT_contra] | None = None,
101
- in_args: InT_contra | Sequence[InT_contra] | None = None,
102
- ctx: RunContext[CtxT] | None = None,
103
+ in_packet: Packet[InT] | None = None,
104
+ in_args: InT | Sequence[InT] | None = None,
105
+ run_id: str | None = None,
103
106
  forgetful: bool = False,
107
+ ctx: RunContext[CtxT] | None = None,
104
108
  ) -> Packet[OutT_co]:
105
109
  packet = in_packet
106
110
  num_iterations = 0
@@ -113,13 +117,14 @@ class LoopedWorkflow(
113
117
  in_packet=packet,
114
118
  in_args=in_args,
115
119
  forgetful=forgetful,
120
+ run_id=self._generate_subproc_run_id(run_id, subproc=subproc),
116
121
  ctx=ctx,
117
122
  )
118
123
 
119
124
  if subproc is self._end_proc:
120
125
  num_iterations += 1
121
126
  exit_packet = cast("Packet[OutT_co]", packet)
122
- if self._exit_workflow_loop_fn(exit_packet, ctx=ctx):
127
+ if self._exit_workflow_loop(exit_packet, ctx=ctx):
123
128
  return exit_packet
124
129
  if num_iterations >= self._max_iterations:
125
130
  logger.info(
@@ -2,15 +2,16 @@ from collections.abc import Sequence
2
2
  from itertools import pairwise
3
3
  from typing import Any, ClassVar, Generic, cast, final
4
4
 
5
+ from ..errors import WorkflowConstructionError
5
6
  from ..packet_pool import Packet, PacketPool
6
7
  from ..processor import Processor
7
8
  from ..run_context import CtxT, RunContext
8
- from ..typing.io import InT_contra, OutT_co, ProcName
9
+ from ..typing.io import InT, OutT_co, ProcName
9
10
  from .workflow_processor import WorkflowProcessor
10
11
 
11
12
 
12
13
  class SequentialWorkflow(
13
- WorkflowProcessor[InT_contra, OutT_co, CtxT], Generic[InT_contra, OutT_co, CtxT]
14
+ WorkflowProcessor[InT, OutT_co, CtxT], Generic[InT, OutT_co, CtxT]
14
15
  ):
15
16
  _generic_arg_to_instance_attr_map: ClassVar[dict[int, str]] = {
16
17
  0: "_in_type",
@@ -23,6 +24,7 @@ class SequentialWorkflow(
23
24
  subprocs: Sequence[Processor[Any, Any, Any, CtxT]],
24
25
  packet_pool: PacketPool[CtxT] | None = None,
25
26
  recipients: list[ProcName] | None = None,
27
+ num_par_run_retries: int = 0,
26
28
  ) -> None:
27
29
  super().__init__(
28
30
  subprocs=subprocs,
@@ -31,11 +33,12 @@ class SequentialWorkflow(
31
33
  name=name,
32
34
  packet_pool=packet_pool,
33
35
  recipients=recipients,
36
+ num_par_run_retries=num_par_run_retries,
34
37
  )
35
38
 
36
39
  for prev_proc, proc in pairwise(subprocs):
37
40
  if prev_proc.out_type != proc.in_type:
38
- raise ValueError(
41
+ raise WorkflowConstructionError(
39
42
  f"Output type {prev_proc.out_type} of subprocessor {prev_proc.name}"
40
43
  f" does not match input type {proc.in_type} of subprocessor"
41
44
  f" {proc.name}"
@@ -46,10 +49,11 @@ class SequentialWorkflow(
46
49
  self,
47
50
  chat_inputs: Any | None = None,
48
51
  *,
49
- in_packet: Packet[InT_contra] | None = None,
50
- in_args: InT_contra | Sequence[InT_contra] | None = None,
51
- ctx: RunContext[CtxT] | None = None,
52
+ in_packet: Packet[InT] | None = None,
53
+ in_args: InT | Sequence[InT] | None = None,
52
54
  forgetful: bool = False,
55
+ run_id: str | None = None,
56
+ ctx: RunContext[CtxT] | None = None,
53
57
  ) -> Packet[OutT_co]:
54
58
  packet = in_packet
55
59
  for subproc in self.subprocs:
@@ -58,6 +62,7 @@ class SequentialWorkflow(
58
62
  in_packet=packet,
59
63
  in_args=in_args,
60
64
  forgetful=forgetful,
65
+ run_id=self._generate_subproc_run_id(run_id, subproc),
61
66
  ctx=ctx,
62
67
  )
63
68
  chat_inputs = None
@@ -3,17 +3,18 @@ from collections.abc import Sequence
3
3
  from typing import Any, ClassVar, Generic
4
4
 
5
5
  from ..comm_processor import CommProcessor
6
+ from ..errors import WorkflowConstructionError
6
7
  from ..packet import Packet
7
8
  from ..packet_pool import PacketPool
8
9
  from ..processor import Processor
9
10
  from ..run_context import CtxT, RunContext
10
- from ..typing.io import InT_contra, OutT_co, ProcName
11
+ from ..typing.io import InT, OutT_co, ProcName
11
12
 
12
13
 
13
14
  class WorkflowProcessor(
14
- CommProcessor[InT_contra, OutT_co, Any, CtxT],
15
+ CommProcessor[InT, OutT_co, Any, CtxT],
15
16
  ABC,
16
- Generic[InT_contra, OutT_co, CtxT],
17
+ Generic[InT, OutT_co, CtxT],
17
18
  ):
18
19
  _generic_arg_to_instance_attr_map: ClassVar[dict[int, str]] = {
19
20
  0: "_in_type",
@@ -24,27 +25,37 @@ class WorkflowProcessor(
24
25
  self,
25
26
  name: ProcName,
26
27
  subprocs: Sequence[Processor[Any, Any, Any, CtxT]],
27
- start_proc: Processor[InT_contra, Any, Any, CtxT],
28
+ start_proc: Processor[InT, Any, Any, CtxT],
28
29
  end_proc: Processor[Any, OutT_co, Any, CtxT],
29
30
  packet_pool: PacketPool[CtxT] | None = None,
30
31
  recipients: list[ProcName] | None = None,
32
+ num_par_run_retries: int = 0,
31
33
  ) -> None:
32
- super().__init__(name=name, packet_pool=packet_pool, recipients=recipients)
34
+ super().__init__(
35
+ name=name,
36
+ packet_pool=packet_pool,
37
+ recipients=recipients,
38
+ num_par_run_retries=num_par_run_retries,
39
+ )
33
40
 
34
41
  if len(subprocs) < 2:
35
- raise ValueError("At least two subprocessors are required")
42
+ raise WorkflowConstructionError("At least two subprocessors are required")
36
43
  if start_proc not in subprocs:
37
- raise ValueError("Start subprocessor must be in the subprocessors list")
44
+ raise WorkflowConstructionError(
45
+ "Start subprocessor must be in the subprocessors list"
46
+ )
38
47
  if end_proc not in subprocs:
39
- raise ValueError("End subprocessor must be in the subprocessors list")
48
+ raise WorkflowConstructionError(
49
+ "End subprocessor must be in the subprocessors list"
50
+ )
40
51
 
41
52
  if start_proc.in_type != self.in_type:
42
- raise ValueError(
53
+ raise WorkflowConstructionError(
43
54
  f"Start subprocessor's input type {start_proc.in_type} does not "
44
55
  f"match workflow's input type {self._in_type}"
45
56
  )
46
57
  if end_proc.out_type != self.out_type:
47
- raise ValueError(
58
+ raise WorkflowConstructionError(
48
59
  f"End subprocessor's output type {end_proc.out_type} does not "
49
60
  f"match workflow's output type {self._out_type}"
50
61
  )
@@ -58,21 +69,27 @@ class WorkflowProcessor(
58
69
  return self._subprocs
59
70
 
60
71
  @property
61
- def start_proc(self) -> Processor[InT_contra, Any, Any, CtxT]:
72
+ def start_proc(self) -> Processor[InT, Any, Any, CtxT]:
62
73
  return self._start_proc
63
74
 
64
75
  @property
65
76
  def end_proc(self) -> Processor[Any, OutT_co, Any, CtxT]:
66
77
  return self._end_proc
67
78
 
79
+ def _generate_subproc_run_id(
80
+ self, run_id: str | None, subproc: Processor[Any, Any, Any, CtxT]
81
+ ) -> str | None:
82
+ return f"{self._generate_run_id(run_id)}/{subproc.name}"
83
+
68
84
  @abstractmethod
69
85
  async def run(
70
86
  self,
71
87
  chat_inputs: Any | None = None,
72
88
  *,
73
- in_packet: Packet[InT_contra] | None = None,
74
- in_args: InT_contra | Sequence[InT_contra] | None = None,
89
+ in_packet: Packet[InT] | None = None,
90
+ in_args: InT | Sequence[InT] | None = None,
75
91
  ctx: RunContext[CtxT] | None = None,
76
92
  forgetful: bool = False,
93
+ run_id: str | None = None,
77
94
  ) -> Packet[OutT_co]:
78
95
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_agents
3
- Version: 0.3.11
3
+ Version: 0.4.2
4
4
  Summary: Grasp Agents Library
5
5
  License-File: LICENSE.md
6
6
  Requires-Python: <4,>=3.11.4
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
45
45
  - Workflows (static communication topology), including loops
46
46
  - Agents-as-tools for task delegation
47
47
  - Freeform A2A communication via the in-process actor model
48
- - Batch processing support outside of agentic loops
48
+ - Parallel processing with flexible retries and rate limiting
49
49
  - Simple logging and usage/cost tracking
50
50
 
51
51
  ## Project Structure
@@ -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
@@ -0,0 +1,50 @@
1
+ grasp_agents/__init__.py,sha256=CIsyUasb9HBC3M4olg6ATAwKXtVNmmtpyGJrt7hpZW4,947
2
+ grasp_agents/cloud_llm.py,sha256=Pnvey9HaRCudbOCx8vtbRYVsiG-Fn-vIssyUafUSq-0,13641
3
+ grasp_agents/comm_processor.py,sha256=2xDZwv80velCyhK04xlQzlaAqj7sdnOfyYOHSaVC54g,7013
4
+ grasp_agents/costs_dict.yaml,sha256=2MFNWtkv5W5WSCcv1Cj13B1iQLVv5Ot9pS_KW2Gu2DA,2510
5
+ grasp_agents/errors.py,sha256=u-P8NZ3S7THQP5Bjv9SFC_JbHd3CKhX3dTWUObEBzmo,461
6
+ grasp_agents/generics_utils.py,sha256=5Pw3I9dlnKC2VGqYKC4ZZUO3Z_vTNT-NPFovNfPkl6I,6542
7
+ grasp_agents/grasp_logging.py,sha256=H1GYhXdQvVkmauFDZ-KDwvVmPQHZUUm9sRqX_ObK2xI,1111
8
+ grasp_agents/http_client.py,sha256=Es8NXGDkp4Nem7g24-jW0KFGA9Hp_o2Cv3cOvjup-iU,859
9
+ grasp_agents/llm.py,sha256=S5muzwTbbT048YyASG_BrMcGYX7IGB0q3KZwL3H26wo,5193
10
+ grasp_agents/llm_agent.py,sha256=tK6cOIPTQzH8GO_xDSLMwRiZX_YrGYNbHzZnURRs8NQ,13835
11
+ grasp_agents/llm_agent_memory.py,sha256=KPVnwF68acVNdSObSaRcO905YYx8vLHJ-kAEfZYs0sA,1860
12
+ grasp_agents/llm_policy_executor.py,sha256=C9qzSkF371-c15-HRQDaDfUaYXKRUCYTgq31b78p2RY,17593
13
+ grasp_agents/memory.py,sha256=QSWjpGTn_t3XFKDxycWUCNFFueQcajzduQGU1vkJFWQ,720
14
+ grasp_agents/packet.py,sha256=PZ1EpclniAoLk7z4ieZbWzgYH3JSRgnlTe_WfbJYG_4,707
15
+ grasp_agents/packet_pool.py,sha256=F29EWkKM-305Qqf3hK9jriYXj4vgAx9iAWTg-hfZONs,3035
16
+ grasp_agents/printer.py,sha256=qxvdUFUu3E5WSjToI4hnXsN0FM8-oIy-pjAybS50oF8,5482
17
+ grasp_agents/processor.py,sha256=DYc6A6z9JE5NU7VESzIfQbHcgu8uvNo6L_SWf_QqHwA,11102
18
+ grasp_agents/prompt_builder.py,sha256=wDcB1UaOH8vdxw1tmWZwVZS_bCdWU0VKQvHLpI8SnCk,7970
19
+ grasp_agents/run_context.py,sha256=pUi2a9WeHUg67cUTQ5T13mmJ3vzQfv-855fWLPeYRmo,1438
20
+ grasp_agents/usage_tracker.py,sha256=3gmgPcB7qHAl5W5Ffnmu_8GOcKBSv3xM1NLy-HMuSlo,3691
21
+ grasp_agents/utils.py,sha256=5WpMEZB42QmXGUwMJjtr3aJOMUZlQust3UtMGXs8Uq0,5501
22
+ grasp_agents/openai/__init__.py,sha256=wpTeew6EjhM6esHCKrEKUpwq0kygMN2QQDxYtmbRG8Y,4201
23
+ grasp_agents/openai/completion_chunk_converters.py,sha256=TFOA7MJniiSV6_lPZsPSg06GlfdWguTrYS2Hd45BjcU,2674
24
+ grasp_agents/openai/completion_converters.py,sha256=vzPEkUOX4l2hobKxZjEk_dyWfzeYesO0DlvWvNVb-Sg,2656
25
+ grasp_agents/openai/content_converters.py,sha256=r1D5uci5x7sbDyl0XN27y-l_jVigCauJruvSdZSnZcc,2510
26
+ grasp_agents/openai/converters.py,sha256=ncscVyPnPMMbyxAfFX3U73lnr_BZU-I89HA5Ld8BuxI,4691
27
+ grasp_agents/openai/message_converters.py,sha256=_fG4vI42rBzoajuC5iYgnUBalg8cQ1ckSt8xFBOuWVY,4111
28
+ grasp_agents/openai/openai_llm.py,sha256=fxJbZA4piNRtr0le6MH1tvOOnxq1o0-OY7gBZrH1G-Q,7993
29
+ grasp_agents/openai/tool_converters.py,sha256=rNH5t2Wir9nuy8Ei0jaxNuzDaXGqTLmLz3VyrnJhyn0,1196
30
+ grasp_agents/rate_limiting/__init__.py,sha256=KRgtF_E7R3YfA2cpYcFcZ7wycV0pWVJ0xRQC7YhiIEQ,158
31
+ grasp_agents/rate_limiting/rate_limiter_chunked.py,sha256=BPgkUXvhmZhTpZs2T6uujNFuxH_kYHiISuf6_-eNhUc,5544
32
+ grasp_agents/rate_limiting/types.py,sha256=PbnNhEAcYedQdIpPJWud8HUVcxa_xZS2RDZu4c5jr40,1003
33
+ grasp_agents/rate_limiting/utils.py,sha256=oEDWDNHYMUdxOOG49PlAJochkZq8nnVBCo6JxPc1iSo,2007
34
+ grasp_agents/typing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ grasp_agents/typing/completion.py,sha256=KrvdFZqOcw5GePdMhDoFuwCkMJ86nqRdz9mT9s2lFv4,2445
36
+ grasp_agents/typing/completion_chunk.py,sha256=eo9h2f3BphBz-sgnCetB9o6Ol6MOl6VH89fMM-NtxtE,6050
37
+ grasp_agents/typing/content.py,sha256=XFmLpNWkGhkw5JujO6UsYwhzTHkU67PfhzaXH2waLcQ,3659
38
+ grasp_agents/typing/converters.py,sha256=kHlocHQS8QnduZOzNPbj3aRD8JpvJd53oudYqWdOxKE,2978
39
+ grasp_agents/typing/events.py,sha256=QbrvXnDmXFr9_cSsdqL9f35eQLOfZ2O0h3a6yCRtKwY,2625
40
+ grasp_agents/typing/io.py,sha256=RtBnxOiEJkfCPz7bEu1T1JXU7o71MhHK3RkeedzWwX0,237
41
+ grasp_agents/typing/message.py,sha256=vB94CLGfNJOZ3pTKneVP_mbZpWrhC_981K5OhHUiLW4,2350
42
+ grasp_agents/typing/tool.py,sha256=mHodyJYbiGQAG7exyDh04n5eqpDEUnV3pFAbC23LHQM,1754
43
+ grasp_agents/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ grasp_agents/workflow/looped_workflow.py,sha256=eQkjCo6j3W5Xc7mw3AGmXpUhTkK2BmhrvCuht2UkZuI,4537
45
+ grasp_agents/workflow/sequential_workflow.py,sha256=UCHZXvTy47S0UG0WwuKp2G7Az6dAcE5tIFPaOXhxcWM,2347
46
+ grasp_agents/workflow/workflow_processor.py,sha256=lGW-Cy1QraU1AaCPVRs9-EtU5Z_3O3EAnuyIPzD6KQ8,3134
47
+ grasp_agents-0.4.2.dist-info/METADATA,sha256=RX_H6TsRQxb-V9JJxjgWu1qn63GPKfEkD2B8Y2cY-1Y,6868
48
+ grasp_agents-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
49
+ grasp_agents-0.4.2.dist-info/licenses/LICENSE.md,sha256=-nNNdWqGB8gJ2O-peFQ2Irshv5tW5pHKyTcYkwvH7CE,1201
50
+ grasp_agents-0.4.2.dist-info/RECORD,,