grasp_agents 0.1.15__py3-none-any.whl → 0.1.17__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/utils.py CHANGED
@@ -1,57 +1,38 @@
1
1
  import ast
2
2
  import asyncio
3
- from datetime import datetime
4
3
  import functools
5
4
  import json
6
5
  import re
7
6
  from collections.abc import Callable, Coroutine
8
7
  from copy import deepcopy
8
+ from datetime import datetime
9
+ from logging import getLogger
9
10
  from pathlib import Path
10
11
  from typing import Any, TypeVar, cast
11
12
 
12
- from pydantic import BaseModel, create_model
13
+ from pydantic import BaseModel, GetCoreSchemaHandler, TypeAdapter, create_model
13
14
  from pydantic.fields import FieldInfo
15
+ from pydantic_core import core_schema
14
16
  from tqdm.autonotebook import tqdm
15
17
 
18
+ logger = getLogger(__name__)
16
19
 
17
- def read_contents_from_file(
18
- file_path: str | Path,
19
- binary_mode: bool = False,
20
- ) -> str:
21
- """Reads and returns contents of file"""
22
- try:
23
- if binary_mode:
24
- with open(file_path, "rb") as file:
25
- return file.read()
26
- else:
27
- with open(file_path) as file:
28
- return file.read()
29
- except FileNotFoundError:
30
- print(f"File {file_path} not found.")
31
- return ""
32
-
33
-
34
- async def asyncio_gather_with_pbar(
35
- *corouts: Coroutine[Any, Any, Any],
36
- no_tqdm: bool = False,
37
- desc: str | None = None,
38
- ) -> list[Any]:
39
- pbar = tqdm(total=len(corouts), desc=desc, disable=no_tqdm)
40
20
 
41
- async def run_and_update(coro: Coroutine[Any, Any, Any]) -> Any:
42
- result = await coro
43
- pbar.update(1)
44
- return result
45
-
46
- wrapped_tasks = [run_and_update(c) for c in corouts]
47
- results = await asyncio.gather(*wrapped_tasks)
48
- pbar.close()
21
+ def merge_pydantic_models(*models: type[BaseModel]) -> type[BaseModel]:
22
+ fields_dict: dict[str, FieldInfo] = {}
23
+ for model in models:
24
+ for field_name, field_info in model.model_fields.items():
25
+ if field_name in fields_dict:
26
+ raise ValueError(
27
+ f"Field conflict detected: '{field_name}' exists in multiple models"
28
+ )
29
+ fields_dict[field_name] = field_info
49
30
 
50
- return results
31
+ return create_model("MergedModel", __module__=__name__, **fields_dict) # type: ignore
51
32
 
52
33
 
53
- def get_timestamp() -> str:
54
- return datetime.now().strftime("%Y%m%d_%H%M%S")
34
+ def filter_fields(data: dict[str, Any], model: type[BaseModel]) -> dict[str, Any]:
35
+ return {key: data[key] for key in model.model_fields if key in data}
55
36
 
56
37
 
57
38
  def read_txt(file_path: str) -> str:
@@ -73,26 +54,32 @@ def format_json_string(text: str) -> str:
73
54
  pass
74
55
  i += 1
75
56
 
76
- return ""
57
+ return text
77
58
 
78
59
 
79
- def read_json_string(json_str: str) -> dict[str, Any] | list[Any]:
60
+ def read_json_string(
61
+ json_str: str, return_none_on_failure: bool = False
62
+ ) -> dict[str, Any] | list[Any] | None:
80
63
  try:
81
64
  json_response = ast.literal_eval(json_str)
82
65
  except (ValueError, SyntaxError):
83
66
  try:
84
67
  json_response = json.loads(json_str)
85
68
  except json.JSONDecodeError as exc:
69
+ if return_none_on_failure:
70
+ return None
86
71
  raise ValueError(
87
72
  "Invalid JSON - Both ast.literal_eval and json.loads "
88
73
  f"failed to parse the following response:\n{json_str}"
89
74
  ) from exc
90
75
 
91
- return json_response # type: ignore
76
+ return json_response
92
77
 
93
78
 
94
- def extract_json(json_str: str) -> dict[str, Any] | list[Any]:
95
- return read_json_string(format_json_string(json_str))
79
+ def extract_json(
80
+ json_str: str, return_none_on_failure: bool = False
81
+ ) -> dict[str, Any] | list[Any] | None:
82
+ return read_json_string(format_json_string(json_str), return_none_on_failure)
96
83
 
97
84
 
98
85
  def extract_xml_list(text: str) -> list[str]:
@@ -105,32 +92,43 @@ def extract_xml_list(text: str) -> list[str]:
105
92
  return chunks
106
93
 
107
94
 
108
- def get_prompt(prompt_text: str | None, prompt_path: str | Path | None) -> str | None:
109
- if prompt_text is None:
110
- prompt = (
111
- read_contents_from_file(prompt_path) if prompt_path is not None else None
112
- )
113
- else:
114
- prompt = prompt_text
115
-
116
- return prompt
117
-
118
-
119
- def merge_pydantic_models(*models: type[BaseModel]) -> type[BaseModel]:
120
- fields_dict: dict[str, FieldInfo] = {}
121
- for model in models:
122
- for field_name, field_info in model.model_fields.items():
123
- if field_name in fields_dict:
124
- raise ValueError(
125
- f"Field conflict detected: '{field_name}' exists in multiple models"
126
- )
127
- fields_dict[field_name] = field_info
128
-
129
- return create_model("MergedModel", __module__=__name__, **fields_dict) # type: ignore
95
+ def make_conditional_parsed_output_type(
96
+ response_format: type, marker: str = "<DONE>"
97
+ ) -> type:
98
+ class ParsedOutput:
99
+ """
100
+ * Accepts any **str**.
101
+ * If the string contains `marker`, it must contain a valid JSON for
102
+ `response_format` → we return that a response_format instance.
103
+ * Otherwise we leave the string untouched.
104
+ """
105
+
106
+ @classmethod
107
+ def __get_pydantic_core_schema__(
108
+ cls,
109
+ _source_type: Any,
110
+ _handler: GetCoreSchemaHandler,
111
+ ) -> core_schema.CoreSchema:
112
+ def validator(v: Any) -> Any:
113
+ if isinstance(v, str) and marker in v:
114
+ v_json_str = format_json_string(v)
115
+ response_format_adapter = TypeAdapter[Any](response_format)
116
+
117
+ return response_format_adapter.validate_json(v_json_str)
118
+
119
+ return v
120
+
121
+ return core_schema.no_info_after_validator_function(
122
+ validator, core_schema.any_schema()
123
+ )
130
124
 
125
+ @classmethod
126
+ def __get_pydantic_json_schema__(
127
+ cls, core_schema: core_schema.CoreSchema, handler: GetCoreSchemaHandler
128
+ ):
129
+ return handler(core_schema)
131
130
 
132
- def filter_fields(data: dict[str, Any], model: type[BaseModel]) -> dict[str, Any]:
133
- return {key: data[key] for key in model.model_fields if key in data}
131
+ return ParsedOutput
134
132
 
135
133
 
136
134
  T = TypeVar("T", bound=Callable[..., Any])
@@ -149,3 +147,54 @@ def forbid_state_change(method: T) -> T:
149
147
  return result
150
148
 
151
149
  return cast("T", wrapper)
150
+
151
+
152
+ def read_contents_from_file(
153
+ file_path: str | Path,
154
+ binary_mode: bool = False,
155
+ ) -> str | bytes:
156
+ """Reads and returns contents of file"""
157
+ try:
158
+ if binary_mode:
159
+ with open(file_path, "rb") as file:
160
+ return file.read()
161
+ else:
162
+ with open(file_path) as file:
163
+ return file.read()
164
+ except FileNotFoundError:
165
+ logger.error(f"File {file_path} not found.")
166
+ return ""
167
+
168
+
169
+ def get_prompt(prompt_text: str | None, prompt_path: str | Path | None) -> str | None:
170
+ if prompt_text is None:
171
+ prompt = (
172
+ read_contents_from_file(prompt_path) if prompt_path is not None else None
173
+ )
174
+ else:
175
+ prompt = prompt_text
176
+
177
+ return prompt # type: ignore[assignment]
178
+
179
+
180
+ async def asyncio_gather_with_pbar(
181
+ *corouts: Coroutine[Any, Any, Any],
182
+ no_tqdm: bool = False,
183
+ desc: str | None = None,
184
+ ) -> list[Any]:
185
+ pbar = tqdm(total=len(corouts), desc=desc, disable=no_tqdm)
186
+
187
+ async def run_and_update(coro: Coroutine[Any, Any, Any]) -> Any:
188
+ result = await coro
189
+ pbar.update(1)
190
+ return result
191
+
192
+ wrapped_tasks = [run_and_update(c) for c in corouts]
193
+ results = await asyncio.gather(*wrapped_tasks)
194
+ pbar.close()
195
+
196
+ return results
197
+
198
+
199
+ def get_timestamp() -> str:
200
+ return datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: grasp_agents
3
+ Version: 0.1.17
4
+ Summary: Grasp Agents Library
5
+ License-File: LICENSE.md
6
+ Requires-Python: <4,>=3.11.4
7
+ Requires-Dist: dotenv>=0.9.9
8
+ Requires-Dist: httpx<1,>=0.27.0
9
+ Requires-Dist: openai<2,>=1.68.2
10
+ Requires-Dist: pydantic>=2
11
+ Requires-Dist: pyyaml>=6.0.2
12
+ Requires-Dist: tenacity>=9.1.2
13
+ Requires-Dist: termcolor<3,>=2.4.0
14
+ Requires-Dist: tqdm<5,>=4.66.2
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Grasp Agents
18
+
19
+ <br/>
20
+ <img src="./.assets/grasp.svg" alt="Grasp Agents" width="320" />
21
+ <br/>
22
+ <br/>
23
+
24
+ [![PyPI version](https://badge.fury.io/py/grasp_agents.svg)](https://badge.fury.io/py/grasp-agents)
25
+ [![License: MIT](https://img.shields.io/badge/license-MIT-yellow?style=flat-square)](https://mit-license.org/)
26
+ [![PyPI downloads](https://img.shields.io/pypi/dm/grasp-agents?style=flat-square)](https://pypi.org/project/grasp-agents/)
27
+ [![GitHub Stars](https://img.shields.io/github/stars/grasp-technologies/grasp-agents?style=social)](https://github.com/grasp-technologies/grasp-agents/stargazers)
28
+ [![GitHub Forks](https://img.shields.io/github/forks/grasp-technologies/grasp-agents?style=social)](https://github.com/grasp-technologies/grasp-agents/network/members)
29
+
30
+ ## Overview
31
+
32
+ **Grasp Agents** is a modular Python framework for building agentic AI pipelines and applications. It is meant to be minimalistic but functional, allowing for rapid experimentation while keeping full and granular low-level control over prompting, LLM handling, and inter-agent communication by avoiding excessive higher-level abstractions.
33
+
34
+ ## Features
35
+
36
+ - Clean formulation of agents as generic entities over:
37
+ * I/O schemas
38
+ * Agent state
39
+ * Shared context
40
+ - Transparent implementation of common agentic patterns:
41
+ * Single-agent loops with an optional "ReAct mode" to enforce reasoning between the tool calls
42
+ * Workflows (static communication topology), including loops
43
+ * Agents-as-tools for task delegation
44
+ * Freeform A2A communication via in-process Actor model
45
+ - Batch processing support outside of agentic loops
46
+ - Simple logging and usage/cost tracking
47
+
48
+ ## Project Structure
49
+
50
+ - `base_agent.py`, `llm_agent.py`, `comm_agent.py`: Core agent class implementations.
51
+ - `agent_message.py`, `agent_message_pool.py`: Messaging and message pool management.
52
+ - `llm_agent_state.py`: State management for LLM agents.
53
+ - `tool_orchestrator.py`: Orchestration of tools used by agents.
54
+ - `prompt_builder.py`: Tools for constructing prompts.
55
+ - `workflow/`: Modules for defining and managing agent workflows.
56
+ - `cloud_llm.py`, `llm.py`: LLM integration and base LLM functionalities.
57
+ - `openai/`: Modules specific to OpenAI API integration.
58
+ - `memory.py`: Memory management for agents (currently only message history).
59
+ - `run_context.py`: Context management for agent runs.
60
+ - `usage_tracker.py`: Tracking of API usage and costs.
61
+ - `costs_dict.yaml`: Dictionary for cost tracking (update if needed).
62
+ - `rate_limiting/`: Basic rate limiting tools.
63
+
64
+ ## Quickstart & Installation Variants (UV Package manager)
65
+
66
+ > **Note:** You can check this sample project code in the [src/grasp_agents/examples/demo/uv](src/grasp_agents/examples/demo/uv) folder. Feel free to copy and paste the code from there to a separate project. There are also [examples](src/grasp_agents/examples/demo/) for other package managers.
67
+
68
+ #### 1. Prerequisites
69
+
70
+ Install the [UV Package Manager](https://github.com/astral-sh/uv):
71
+
72
+ ```bash
73
+ curl -LsSf https://astral.sh/uv/install.sh | sh
74
+ ```
75
+
76
+ #### 2. Create Project & Install Dependencies
77
+
78
+ ```bash
79
+ mkdir my-test-uv-app
80
+ cd my-test-uv-app
81
+ uv init .
82
+ ```
83
+
84
+ Create and activate a virtual environment:
85
+
86
+ ```bash
87
+ uv venv
88
+ source .venv/bin/activate
89
+ ```
90
+
91
+ Add and sync dependencies:
92
+
93
+ ```bash
94
+ uv add grasp_agents
95
+ uv sync
96
+ ```
97
+
98
+ #### 3. Example Usage
99
+
100
+ Ensure you have a `.env` file with your OpenAI and Google AI Studio API keys set
101
+
102
+ ```
103
+ OPENAI_API_KEY=your_openai_api_key
104
+ GOOGLE_AI_STUDIO_API_KEY=your_google_ai_studio_api_key
105
+ ```
106
+
107
+ Create a script, e.g., `problem_recommender.py`:
108
+
109
+ ```python
110
+ import re
111
+ from typing import Any
112
+ from pathlib import Path
113
+ from pydantic import BaseModel, Field
114
+ from dotenv import load_dotenv
115
+ from grasp_agents.typing.tool import BaseTool
116
+ from grasp_agents.typing.io import AgentPayload
117
+ from grasp_agents.run_context import RunContextWrapper
118
+ from grasp_agents.openai.openai_llm import OpenAILLM, OpenAILLMSettings
119
+ from grasp_agents.llm_agent import LLMAgent
120
+ from grasp_agents.grasp_logging import setup_logging
121
+ from grasp_agents.typing.message import Conversation
122
+
123
+ load_dotenv()
124
+
125
+
126
+ # Configure the logger to output to the console and/or a file
127
+ setup_logging(
128
+ logs_file_path="grasp_agents_demo.log",
129
+ logs_config_path=Path().cwd() / "configs/logging/default.yaml",
130
+ )
131
+
132
+ sys_prompt_react = """
133
+ Your task is to suggest an exciting stats problem to a student.
134
+ Ask the student about their education, interests, and preferences, then suggest a problem tailored to them.
135
+
136
+ # Instructions
137
+ * Ask questions one by one.
138
+ * Provide your thinking before asking a question and after receiving a reply.
139
+ * The problem must be enclosed in <PROBLEM> tags.
140
+ """
141
+
142
+
143
+ class TeacherQuestion(BaseModel):
144
+ question: str = Field(..., description="The question to ask the student.")
145
+
146
+ StudentReply = str
147
+
148
+
149
+ class AskStudentTool(BaseTool[TeacherQuestion, StudentReply, Any]):
150
+ name: str = "ask_student_tool"
151
+ description: str = "Ask the student a question and get their reply."
152
+ in_schema: type[TeacherQuestion] = TeacherQuestion
153
+ out_schema: type[StudentReply] = StudentReply
154
+
155
+ async def run(
156
+ self, inp: TeacherQuestion, ctx: RunContextWrapper[Any] | None = None
157
+ ) -> StudentReply:
158
+ return input(inp.question)
159
+
160
+
161
+ class FinalResponse(AgentPayload):
162
+ problem: str
163
+
164
+
165
+ teacher = LLMAgent[Any, FinalResponse, None](
166
+ agent_id="teacher",
167
+ llm=OpenAILLM(
168
+ model_name="gpt-4.1",
169
+ api_provider="openai",
170
+ llm_settings=OpenAILLMSettings(temperature=0.1),
171
+ ),
172
+ tools=[AskStudentTool()],
173
+ max_turns=20,
174
+ react_mode=True,
175
+ sys_prompt=sys_prompt_react,
176
+ out_schema=FinalResponse,
177
+ set_state_strategy="reset",
178
+ )
179
+
180
+
181
+ @teacher.tool_call_loop_exit_handler
182
+ def exit_tool_call_loop(conversation: Conversation, ctx, **kwargs) -> None:
183
+ message_text = conversation[-1].content
184
+
185
+ return re.search(r"<PROBLEM>", message_text)
186
+
187
+
188
+ @teacher.parse_output_handler
189
+ def parse_output(conversation: Conversation, ctx, **kwargs) -> FinalResponse:
190
+ message_text = conversation[-1].content
191
+ matches = re.findall(r"<PROBLEM>(.*?)</PROBLEM>", message_text, re.DOTALL)
192
+
193
+ return FinalResponse(problem=matches[0])
194
+
195
+
196
+ async def main():
197
+ ctx = RunContextWrapper(print_messages=True)
198
+ out = await teacher.run(ctx=ctx)
199
+ print(out.payloads[0].problem)
200
+ print(ctx.usage_tracker.total_usage)
201
+
202
+
203
+ asyncio.run(main())
204
+ ```
205
+
206
+ Run your script:
207
+
208
+ ```bash
209
+ uv run problem_recommender.py
210
+ ```
211
+
212
+ You can find more examples in [src/grasp_agents/examples/notebooks/agents_demo.ipynb](src/grasp_agents/examples/notebooks/agents_demo.ipynb).
@@ -0,0 +1,44 @@
1
+ grasp_agents/agent_message.py,sha256=Z-czIHNSe7eAo5r6q2Zu10HFpUjQjVbayyglGyyj4Lw,911
2
+ grasp_agents/agent_message_pool.py,sha256=4O4xz-aZ_I5m3iLAUiMAQcVn5AGRP4daUM8XME03Xsw,3250
3
+ grasp_agents/base_agent.py,sha256=4mNMyWdt9MTzg64JhrVjcU_FSYDj0ED_AKpRZXCRVb4,1870
4
+ grasp_agents/cloud_llm.py,sha256=JJmIUu8SOJ4TElQ4B19mPgXJ1SiapZdPFFhA2YVRKRw,13015
5
+ grasp_agents/comm_agent.py,sha256=ukkT0zXgE8-yt63fyi3MQ_3Q0QjwIDHKz4MbHt9FBxw,7323
6
+ grasp_agents/costs_dict.yaml,sha256=EW6XxRXLZobMwQEEiUNYALbDzfbZFb2zEVCaTSAqYjw,2334
7
+ grasp_agents/grasp_logging.py,sha256=H1GYhXdQvVkmauFDZ-KDwvVmPQHZUUm9sRqX_ObK2xI,1111
8
+ grasp_agents/http_client.py,sha256=KZva2MjJjuI5ohUeU8RdTAImUnQYaqBrV2jDH8smbJw,738
9
+ grasp_agents/llm.py,sha256=RZY25UNJEdPUmqOHifUrrQTgfoDCAVtZN9WQvvxnLC4,3004
10
+ grasp_agents/llm_agent.py,sha256=_D16rA8LDhALZKYYuWxB-E75g3uBgzM04EcJyuRMKZA,12619
11
+ grasp_agents/llm_agent_state.py,sha256=91K1-8Uodbe-t_I6nu0xBzHfQjssZYCHjMuDbu5aCr0,2327
12
+ grasp_agents/memory.py,sha256=X1YtVX8XxP5KnGPMW8BqjID8QK4hTG2obxoyhnnZ4pU,5575
13
+ grasp_agents/printer.py,sha256=Jk6OJExio53gbKBod5Dd8Y3CWYrVb4K5q4UJ8i9cQvo,5024
14
+ grasp_agents/prompt_builder.py,sha256=rYVIY4adJwBitjrTYvpEh5x8C7cLbIiXxT1F-vQuvEM,7393
15
+ grasp_agents/run_context.py,sha256=hyETO3-p0azPFws75kX6rrUDLf58Ar6jmyt6TQ5Po78,2589
16
+ grasp_agents/tool_orchestrator.py,sha256=6VX8FGYeUHiSt9GpUjOga7KGP55Kzs1jEiNcOZysIAo,5501
17
+ grasp_agents/usage_tracker.py,sha256=5YuN6hpg6HASdg-hOylgWzhCiORmDMnZuQtbISfhm_4,3378
18
+ grasp_agents/utils.py,sha256=-OdCs27FKek8owGbwmAVWwLXDgEzoTu-B5_EwRLEsz4,5869
19
+ grasp_agents/openai/__init__.py,sha256=qN8HMAatSJKOsA6v-JwakMYguwkswCVHqrmK1gFy9wI,3096
20
+ grasp_agents/openai/completion_converters.py,sha256=lX9h1kaGAo5ttsl-4V7l4x8IpjxJaJJtyU2cKu3-EOc,1871
21
+ grasp_agents/openai/content_converters.py,sha256=6GI0D7xJalzsiawAJOyCUzTJTo0NQdpv87YKmfN0LYQ,2631
22
+ grasp_agents/openai/converters.py,sha256=DBXBxow9oRG6pc8inpZBLiuUqHzVfpscmHFpN9bAdvc,5276
23
+ grasp_agents/openai/message_converters.py,sha256=KjF6FbXzwlWdM-1YT3cswUV-74sjiwOhLFPMY4sJ5Xk,4593
24
+ grasp_agents/openai/openai_llm.py,sha256=dscGlVhH4v1yAw4NRPgJdww9toOoMRpIqA6HD4IGWOs,6132
25
+ grasp_agents/openai/tool_converters.py,sha256=KhWRETkjhjocISUo_HBZ8QfBiyTOoC5WurPNAR4BYxc,1027
26
+ grasp_agents/rate_limiting/__init__.py,sha256=KRgtF_E7R3YfA2cpYcFcZ7wycV0pWVJ0xRQC7YhiIEQ,158
27
+ grasp_agents/rate_limiting/rate_limiter_chunked.py,sha256=jp6bqWGq2qYAxbmyU_bAGeBXM_nEmSrR6VabDutcTA0,5800
28
+ grasp_agents/rate_limiting/types.py,sha256=JbLYJC-gmzcHH_4-YNTz9IcIwVpcpDyDGvljxNznf5k,1389
29
+ grasp_agents/rate_limiting/utils.py,sha256=yrqboDWWqeekDcs0xaeOPOvelhgAlywDpQpscpPqlhg,1995
30
+ grasp_agents/typing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ grasp_agents/typing/completion.py,sha256=_KDLx3Gtz7o-pEZrvAFgCZwDmkr2oQkxrL-2LSXHHsw,657
32
+ grasp_agents/typing/content.py,sha256=13nLNZqZgtpo9sM0vCRQmZ4bQjjZqUSElMQOwjL7bO8,3651
33
+ grasp_agents/typing/converters.py,sha256=EbGur_Ngx-wINfyOEHa7JqmKnMAxZ5vJjhAPoqBf_AM,3048
34
+ grasp_agents/typing/io.py,sha256=M294tNROa9gJ_I38c9pLekEbUK2p3qirYdE_QGpuw1c,624
35
+ grasp_agents/typing/message.py,sha256=oCpqD_CV2Da-M-l-e5liFJSwK8267fxfcU68LIc7C1E,3801
36
+ grasp_agents/typing/tool.py,sha256=t40vr3ljQY_Qx0f0KZIc151bYlHUF7Bf7JfOkiJOi2c,1657
37
+ grasp_agents/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ grasp_agents/workflow/looped_agent.py,sha256=YBOgOIvy3_NwKvEoGgzQJ2fY9SNG66MQk6obSBGWvCc,3896
39
+ grasp_agents/workflow/sequential_agent.py,sha256=yDt2nA-b1leVByD8jsKrWD6bHe0o9z33jrOJGOLwbyk,2004
40
+ grasp_agents/workflow/workflow_agent.py,sha256=9U94IQ39Vb1W_5u8aoqHb65ikdarEhEJkexDz8xwHD4,2294
41
+ grasp_agents-0.1.17.dist-info/METADATA,sha256=YG_CGwTAOAQFAOA_Aqi6pqwF_ODYtzY_nQEgf2IRMFM,7001
42
+ grasp_agents-0.1.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ grasp_agents-0.1.17.dist-info/licenses/LICENSE.md,sha256=Kfeo0gdlLS6tLQiWwO9UWhjp9-f93a5kShSiBp2FG-c,1201
44
+ grasp_agents-0.1.17.dist-info/RECORD,,
@@ -1,152 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: grasp_agents
3
- Version: 0.1.15
4
- Summary: Grasp Agents Library
5
- License-File: LICENSE.md
6
- Requires-Python: <4,>=3.11.4
7
- Requires-Dist: dotenv>=0.9.9
8
- Requires-Dist: httpx<1,>=0.27.0
9
- Requires-Dist: openai<2,>=1.68.2
10
- Requires-Dist: pyyaml>=6.0.2
11
- Requires-Dist: tenacity>=9.1.2
12
- Requires-Dist: termcolor<3,>=2.4.0
13
- Requires-Dist: tqdm<5,>=4.66.2
14
- Description-Content-Type: text/markdown
15
-
16
- # Grasp Agents
17
-
18
- <br/>
19
- <img src="./.assets/grasp.svg" alt="Grasp Agents" width="320" />
20
- <br/>
21
- <br/>
22
-
23
- [![PyPI version](https://badge.fury.io/py/grasp_agents.svg)](https://badge.fury.io/py/grasp-agents)
24
- [![License: MIT](https://img.shields.io/badge/license-MIT-yellow?style=flat-square)](https://mit-license.org/)
25
- [![PyPI downloads](https://img.shields.io/pypi/dm/grasp-agents?style=flat-square)](https://pypi.org/project/grasp-agents/)
26
- [![GitHub Stars](https://img.shields.io/github/stars/grasp-technologies/grasp-agents?style=social)](https://github.com/grasp-technologies/grasp-agents/stargazers)
27
- [![GitHub Forks](https://img.shields.io/github/forks/grasp-technologies/grasp-agents?style=social)](https://github.com/grasp-technologies/grasp-agents/network/members)
28
-
29
- ## Overview
30
-
31
- **Grasp Agents** is a modular Python framework for building agentic AI pipelines and applications. It provides reusable agent classes, message handling, LLM integration, memory, and orchestration utilities. The framework is designed for flexibility, composability, and clarity, enabling rapid prototyping and robust development of multi-agent systems.
32
-
33
- ## Features
34
-
35
- - Modular agent base classes
36
- - Message and memory management
37
- - LLM and tool orchestration
38
- - Logging and usage tracking
39
- - Extensible architecture
40
-
41
- ## Project Structure
42
-
43
- - `src/grasp_agents/` — Core framework modules
44
- - `base_agent.py`, `llm_agent.py`, `comm_agent.py`: Agent classes
45
- - `agent_message.py`, `agent_message_pool.py`: Messaging
46
- - `memory.py`: Memory management
47
- - `cloud_llm.py`, `llm.py`: LLM integration
48
- - `tool_orchestrator.py`: Tool orchestration
49
- - `usage_tracker.py`, `grasp_logging.py`: Usage and logging
50
- - `data_retrieval/`, `openai/`, `typing/`, `workflow/`: Extensions and utilities
51
- - `configs/` — Configuration files
52
- - `data/` — Logs and datasets
53
-
54
- ## Quickstart & Installation Variants (UV Package manager)
55
-
56
- ### Option 1: UV Package Manager Project
57
-
58
- > **Note:** You can check this sample project code in the [src/grasp_agents/examples/demo/uv](src/grasp_agents/examples/demo/uv) folder. Feel free to copy and paste the code from there to a separate project. There are also [examples](src/grasp_agents/examples/demo/) for other package managers.
59
-
60
- #### 1. Prerequisites
61
-
62
- Install the [UV Package Manager](https://github.com/astral-sh/uv):
63
-
64
- ```bash
65
- curl -LsSf https://astral.sh/uv/install.sh | sh
66
- ```
67
-
68
- #### 2. Create Project & Install Dependencies
69
-
70
- ```bash
71
- mkdir my-test-uv-app
72
- cd my-test-uv-app
73
- uv init .
74
- ```
75
-
76
- Create and activate a virtual environment:
77
-
78
- ```bash
79
- uv venv
80
- source .venv/bin/activate
81
- ```
82
-
83
- Add and sync dependencies:
84
-
85
- ```bash
86
- uv add grasp_agents
87
- uv sync
88
- ```
89
-
90
- #### 3. Example Usage
91
-
92
- Create a file, e.g., `hello.py`:
93
-
94
- Ensure you have a `.env` file with your OpenAI and Google AI Studio API keys set
95
-
96
- ```
97
- OPENAI_API_KEY=your_openai_api_key
98
- GOOGLE_AI_STUDIO_API_KEY=your_google_ai_studio_api_key
99
- ```
100
-
101
- ```python
102
- import asyncio
103
- from typing import Any
104
-
105
- from grasp_agents.llm_agent import LLMAgent
106
- from grasp_agents.openai.openai_llm import (
107
- OpenAILLM,
108
- OpenAILLMSettings,
109
- )
110
- from grasp_agents.typing.io import (
111
- AgentPayload,
112
- )
113
- from grasp_agents.run_context import RunContextWrapper
114
-
115
- from dotenv import load_dotenv
116
-
117
- load_dotenv()
118
-
119
- class Response(AgentPayload):
120
- response: str
121
-
122
-
123
- chatbot = LLMAgent[Any, Response, None](
124
- agent_id="chatbot",
125
- llm=OpenAILLM(
126
- model_name="gpt-4o",
127
- llm_settings=OpenAILLMSettings(),
128
- ),
129
- sys_prompt=None,
130
- out_schema=Response,
131
- )
132
-
133
-
134
- @chatbot.parse_output_handler
135
- def output_handler(conversation, ctx, **kwargs) -> Response:
136
- return Response(response=conversation[-1].content)
137
-
138
-
139
- async def main():
140
- ctx = RunContextWrapper(print_messages=True)
141
- out = await chatbot.run("Hello, agent!", ctx=ctx)
142
- print(out.payloads[0].response)
143
-
144
-
145
- asyncio.run(main())
146
- ```
147
-
148
- Run your script:
149
-
150
- ```bash
151
- uv run hello.py
152
- ```