grasp_agents 0.1.16__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/cloud_llm.py CHANGED
@@ -16,13 +16,13 @@ from tenacity import (
16
16
  )
17
17
  from typing_extensions import TypedDict
18
18
 
19
- from .data_retrieval.rate_limiter_chunked import ( # type: ignore
20
- RateLimiterC,
21
- limit_rate_chunked,
22
- )
23
19
  from .http_client import AsyncHTTPClientParams, create_async_http_client
24
20
  from .llm import LLM, ConvertT, LLMSettings, SettingsT
25
21
  from .memory import MessageHistory
22
+ from .rate_limiting.rate_limiter_chunked import ( # type: ignore
23
+ RateLimiterC,
24
+ limit_rate_chunked,
25
+ )
26
26
  from .typing.completion import Completion, CompletionChunk
27
27
  from .typing.message import AssistantMessage, Conversation
28
28
  from .typing.tool import BaseTool, ToolChoice
@@ -3,14 +3,14 @@ from collections.abc import Iterable
3
3
  from copy import deepcopy
4
4
  from typing import Any, Literal
5
5
 
6
- from openai import AsyncOpenAI
7
- from openai._types import NOT_GIVEN # noqa: PLC2701 # type: ignore[import]
8
6
  from pydantic import BaseModel
9
7
 
10
- from ..data_retrieval.rate_limiter_chunked import RateLimiterC
8
+ from openai import AsyncOpenAI
9
+ from openai._types import NOT_GIVEN # type: ignore[import]
11
10
 
12
11
  from ..cloud_llm import APIProvider, CloudLLM, CloudLLMSettings
13
12
  from ..http_client import AsyncHTTPClientParams
13
+ from ..rate_limiting.rate_limiter_chunked import RateLimiterC
14
14
  from ..typing.message import AssistantMessage, Conversation
15
15
  from ..typing.tool import BaseTool
16
16
  from . import (
@@ -3,16 +3,11 @@ import functools
3
3
  import logging
4
4
  from collections.abc import Callable, Coroutine
5
5
  from time import monotonic
6
- from typing import (
7
- Any,
8
- Generic,
9
- overload,
10
- )
6
+ from typing import Any, Generic, overload
11
7
 
12
8
  from tqdm.autonotebook import tqdm
13
9
 
14
10
  from ..utils import asyncio_gather_with_pbar
15
-
16
11
  from .types import (
17
12
  QueryP,
18
13
  QueryR,
@@ -54,9 +49,7 @@ class RateLimiterC(Generic[QueryT, QueryR]):
54
49
  if now < self._state.next_request_time:
55
50
  await asyncio.sleep(self._state.next_request_time - now)
56
51
  self._state.next_request_time = monotonic() + 1.01 * 60.0 / self._rpm
57
- result = await func_partial(inp)
58
-
59
- return result
52
+ return await func_partial(inp)
60
53
 
61
54
  async def process_inputs(
62
55
  self,
@@ -14,11 +14,16 @@ from .types import (
14
14
 
15
15
 
16
16
  def is_bound_method(func: Callable[..., Any], self_candidate: Any) -> bool:
17
- return (inspect.ismethod(func) and (func.__self__ is self_candidate)) or hasattr(self_candidate, func.__name__)
17
+ return (inspect.ismethod(func) and (func.__self__ is self_candidate)) or hasattr(
18
+ self_candidate, func.__name__
19
+ )
18
20
 
19
21
 
20
22
  def split_pos_args(
21
- call: (RetrievalCallableSingle[QueryT, QueryP, QueryR] | RetrievalCallableList[QueryT, QueryP, QueryR]),
23
+ call: (
24
+ RetrievalCallableSingle[QueryT, QueryP, QueryR]
25
+ | RetrievalCallableList[QueryT, QueryP, QueryR]
26
+ ),
22
27
  args: Sequence[Any],
23
28
  ) -> tuple[Any | None, QueryT | list[QueryT], Sequence[Any]]:
24
29
  if not args:
@@ -28,12 +33,15 @@ def split_pos_args(
28
33
  # Case: Bound instance method with signature (self, inp, *rest)
29
34
  if len(args) < 2:
30
35
  raise ValueError(
31
- "Must pass at least `self` and an input (or a list of inputs) " + "for a bound instance method."
36
+ "Must pass at least `self` and an input (or a list of inputs) "
37
+ "for a bound instance method."
32
38
  )
33
39
  return maybe_self, args[1], args[2:]
34
40
  # Case: Standalone function with signature (inp, *rest)
35
41
  if not args:
36
- raise ValueError("Must pass an input (or a list of inputs) " + "for a standalone function.")
42
+ raise ValueError(
43
+ "Must pass an input (or a list of inputs) " + "for a standalone function."
44
+ )
37
45
  return None, args[0], args[1:]
38
46
 
39
47
 
@@ -53,5 +61,7 @@ def partial_retrieval_callable(
53
61
  return wrapper
54
62
 
55
63
 
56
- def expected_exec_time_from_max_concurrency_and_rpm(rpm: float, max_concurrency: int) -> float:
64
+ def expected_exec_time_from_max_concurrency_and_rpm(
65
+ rpm: float, max_concurrency: int
66
+ ) -> float:
57
67
  return 60.0 / (rpm / max_concurrency)
grasp_agents/utils.py CHANGED
@@ -1,20 +1,19 @@
1
1
  import ast
2
- import functools
3
2
  import asyncio
4
- from datetime import datetime
3
+ import functools
5
4
  import json
6
5
  import re
7
- from logging import getLogger
8
- from collections.abc import Callable
6
+ from collections.abc import Callable, Coroutine
9
7
  from copy import deepcopy
8
+ from datetime import datetime
9
+ from logging import getLogger
10
10
  from pathlib import Path
11
- from typing import Any, TypeVar, cast, Coroutine
12
- from tqdm.autonotebook import tqdm
11
+ from typing import Any, TypeVar, cast
13
12
 
14
13
  from pydantic import BaseModel, GetCoreSchemaHandler, TypeAdapter, create_model
15
14
  from pydantic.fields import FieldInfo
16
15
  from pydantic_core import core_schema
17
-
16
+ from tqdm.autonotebook import tqdm
18
17
 
19
18
  logger = getLogger(__name__)
20
19
 
@@ -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).
@@ -1,7 +1,7 @@
1
1
  grasp_agents/agent_message.py,sha256=Z-czIHNSe7eAo5r6q2Zu10HFpUjQjVbayyglGyyj4Lw,911
2
2
  grasp_agents/agent_message_pool.py,sha256=4O4xz-aZ_I5m3iLAUiMAQcVn5AGRP4daUM8XME03Xsw,3250
3
3
  grasp_agents/base_agent.py,sha256=4mNMyWdt9MTzg64JhrVjcU_FSYDj0ED_AKpRZXCRVb4,1870
4
- grasp_agents/cloud_llm.py,sha256=q72bDyC9bxQdiO552LiOYr58B9T3bMAJu0TGUZH4Lfk,13016
4
+ grasp_agents/cloud_llm.py,sha256=JJmIUu8SOJ4TElQ4B19mPgXJ1SiapZdPFFhA2YVRKRw,13015
5
5
  grasp_agents/comm_agent.py,sha256=ukkT0zXgE8-yt63fyi3MQ_3Q0QjwIDHKz4MbHt9FBxw,7323
6
6
  grasp_agents/costs_dict.yaml,sha256=EW6XxRXLZobMwQEEiUNYALbDzfbZFb2zEVCaTSAqYjw,2334
7
7
  grasp_agents/grasp_logging.py,sha256=H1GYhXdQvVkmauFDZ-KDwvVmPQHZUUm9sRqX_ObK2xI,1111
@@ -15,18 +15,18 @@ grasp_agents/prompt_builder.py,sha256=rYVIY4adJwBitjrTYvpEh5x8C7cLbIiXxT1F-vQuvE
15
15
  grasp_agents/run_context.py,sha256=hyETO3-p0azPFws75kX6rrUDLf58Ar6jmyt6TQ5Po78,2589
16
16
  grasp_agents/tool_orchestrator.py,sha256=6VX8FGYeUHiSt9GpUjOga7KGP55Kzs1jEiNcOZysIAo,5501
17
17
  grasp_agents/usage_tracker.py,sha256=5YuN6hpg6HASdg-hOylgWzhCiORmDMnZuQtbISfhm_4,3378
18
- grasp_agents/utils.py,sha256=tacUTUnPqz8qUvJxGpGhcdXOyvZJOzfblCrtzqfkCj8,5870
19
- grasp_agents/data_retrieval/__init__.py,sha256=KRgtF_E7R3YfA2cpYcFcZ7wycV0pWVJ0xRQC7YhiIEQ,158
20
- grasp_agents/data_retrieval/rate_limiter_chunked.py,sha256=NPqYrWwKTx1lim_zlhWar5wDwFz1cA-b6JOzT10kOtE,5843
21
- grasp_agents/data_retrieval/types.py,sha256=JbLYJC-gmzcHH_4-YNTz9IcIwVpcpDyDGvljxNznf5k,1389
22
- grasp_agents/data_retrieval/utils.py,sha256=D3Bkq6-9gF7ubearjZwZaTt_u2-sM3JDlGQD9HmJ3rQ,1917
18
+ grasp_agents/utils.py,sha256=-OdCs27FKek8owGbwmAVWwLXDgEzoTu-B5_EwRLEsz4,5869
23
19
  grasp_agents/openai/__init__.py,sha256=qN8HMAatSJKOsA6v-JwakMYguwkswCVHqrmK1gFy9wI,3096
24
20
  grasp_agents/openai/completion_converters.py,sha256=lX9h1kaGAo5ttsl-4V7l4x8IpjxJaJJtyU2cKu3-EOc,1871
25
21
  grasp_agents/openai/content_converters.py,sha256=6GI0D7xJalzsiawAJOyCUzTJTo0NQdpv87YKmfN0LYQ,2631
26
22
  grasp_agents/openai/converters.py,sha256=DBXBxow9oRG6pc8inpZBLiuUqHzVfpscmHFpN9bAdvc,5276
27
23
  grasp_agents/openai/message_converters.py,sha256=KjF6FbXzwlWdM-1YT3cswUV-74sjiwOhLFPMY4sJ5Xk,4593
28
- grasp_agents/openai/openai_llm.py,sha256=rHeix_lSH8mogPfHjgrsaYMrHBMt5q9k-4x33_-zwyA,6149
24
+ grasp_agents/openai/openai_llm.py,sha256=dscGlVhH4v1yAw4NRPgJdww9toOoMRpIqA6HD4IGWOs,6132
29
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
30
  grasp_agents/typing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  grasp_agents/typing/completion.py,sha256=_KDLx3Gtz7o-pEZrvAFgCZwDmkr2oQkxrL-2LSXHHsw,657
32
32
  grasp_agents/typing/content.py,sha256=13nLNZqZgtpo9sM0vCRQmZ4bQjjZqUSElMQOwjL7bO8,3651
@@ -38,7 +38,7 @@ grasp_agents/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
38
38
  grasp_agents/workflow/looped_agent.py,sha256=YBOgOIvy3_NwKvEoGgzQJ2fY9SNG66MQk6obSBGWvCc,3896
39
39
  grasp_agents/workflow/sequential_agent.py,sha256=yDt2nA-b1leVByD8jsKrWD6bHe0o9z33jrOJGOLwbyk,2004
40
40
  grasp_agents/workflow/workflow_agent.py,sha256=9U94IQ39Vb1W_5u8aoqHb65ikdarEhEJkexDz8xwHD4,2294
41
- grasp_agents-0.1.16.dist-info/METADATA,sha256=s8QElgMOrmr0ZXlfWgRvPbz-kyfDtUBPnCu2EPCUsBY,4254
42
- grasp_agents-0.1.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- grasp_agents-0.1.16.dist-info/licenses/LICENSE.md,sha256=Kfeo0gdlLS6tLQiWwO9UWhjp9-f93a5kShSiBp2FG-c,1201
44
- grasp_agents-0.1.16.dist-info/RECORD,,
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,151 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: grasp_agents
3
- Version: 0.1.16
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 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.
33
-
34
- ## Features
35
-
36
- - Modular agent base classes
37
- - Message and memory management
38
- - LLM and tool orchestration
39
- - Logging and usage tracking
40
- - Extensible architecture
41
-
42
- ## Project Structure
43
-
44
- - `src/grasp_agents/` — Core framework modules
45
- - `base_agent.py`, `llm_agent.py`, `comm_agent.py`: Agent classes
46
- - `agent_message.py`, `agent_message_pool.py`: Messaging
47
- - `memory.py`: Memory management
48
- - `cloud_llm.py`, `llm.py`: LLM integration
49
- - `tool_orchestrator.py`: Tool orchestration
50
- - `usage_tracker.py`, `grasp_logging.py`: Usage and logging
51
- - `data_retrieval/`, `openai/`, `typing/`, `workflow/`: Extensions and utilities
52
- - `configs/` — Configuration files
53
- - `data/` — Logs and datasets
54
-
55
- ## Quickstart & Installation Variants (UV Package manager)
56
-
57
- > **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.
58
-
59
- #### 1. Prerequisites
60
-
61
- Install the [UV Package Manager](https://github.com/astral-sh/uv):
62
-
63
- ```bash
64
- curl -LsSf https://astral.sh/uv/install.sh | sh
65
- ```
66
-
67
- #### 2. Create Project & Install Dependencies
68
-
69
- ```bash
70
- mkdir my-test-uv-app
71
- cd my-test-uv-app
72
- uv init .
73
- ```
74
-
75
- Create and activate a virtual environment:
76
-
77
- ```bash
78
- uv venv
79
- source .venv/bin/activate
80
- ```
81
-
82
- Add and sync dependencies:
83
-
84
- ```bash
85
- uv add grasp_agents
86
- uv sync
87
- ```
88
-
89
- #### 3. Example Usage
90
-
91
- Create a file, e.g., `hello.py`:
92
-
93
- Ensure you have a `.env` file with your OpenAI and Google AI Studio API keys set
94
-
95
- ```
96
- OPENAI_API_KEY=your_openai_api_key
97
- GOOGLE_AI_STUDIO_API_KEY=your_google_ai_studio_api_key
98
- ```
99
-
100
- ```python
101
- import asyncio
102
- from typing import Any
103
-
104
- from grasp_agents.llm_agent import LLMAgent
105
- from grasp_agents.openai.openai_llm import (
106
- OpenAILLM,
107
- OpenAILLMSettings,
108
- )
109
- from grasp_agents.typing.io import (
110
- AgentPayload,
111
- )
112
- from grasp_agents.run_context import RunContextWrapper
113
-
114
- from dotenv import load_dotenv
115
-
116
- load_dotenv()
117
-
118
- class Response(AgentPayload):
119
- response: str
120
-
121
-
122
- chatbot = LLMAgent[Any, Response, None](
123
- agent_id="chatbot",
124
- llm=OpenAILLM(
125
- model_name="gpt-4o",
126
- llm_settings=OpenAILLMSettings(),
127
- ),
128
- sys_prompt=None,
129
- out_schema=Response,
130
- )
131
-
132
-
133
- @chatbot.parse_output_handler
134
- def output_handler(conversation, ctx, **kwargs) -> Response:
135
- return Response(response=conversation[-1].content)
136
-
137
-
138
- async def main():
139
- ctx = RunContextWrapper(print_messages=True)
140
- out = await chatbot.run("Hello, agent!", ctx=ctx)
141
- print(out.payloads[0].response)
142
-
143
-
144
- asyncio.run(main())
145
- ```
146
-
147
- Run your script:
148
-
149
- ```bash
150
- uv run hello.py
151
- ```
File without changes