grasp_agents 0.1.16__py3-none-any.whl → 0.1.18__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 +4 -4
- grasp_agents/openai/openai_llm.py +3 -3
- grasp_agents/{data_retrieval → rate_limiting}/rate_limiter_chunked.py +2 -9
- grasp_agents/{data_retrieval → rate_limiting}/utils.py +15 -5
- grasp_agents/utils.py +6 -7
- grasp_agents-0.1.18.dist-info/METADATA +212 -0
- {grasp_agents-0.1.16.dist-info → grasp_agents-0.1.18.dist-info}/RECORD +11 -11
- grasp_agents-0.1.16.dist-info/METADATA +0 -151
- /grasp_agents/{data_retrieval → rate_limiting}/__init__.py +0 -0
- /grasp_agents/{data_retrieval → rate_limiting}/types.py +0 -0
- {grasp_agents-0.1.16.dist-info → grasp_agents-0.1.18.dist-info}/WHEEL +0 -0
- {grasp_agents-0.1.16.dist-info → grasp_agents-0.1.18.dist-info}/licenses/LICENSE.md +0 -0
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
|
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
|
-
|
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(
|
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: (
|
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) "
|
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(
|
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(
|
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
|
-
|
3
|
+
import functools
|
5
4
|
import json
|
6
5
|
import re
|
7
|
-
from
|
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
|
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.18
|
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,>=8.3.0
|
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
|
+
[](https://badge.fury.io/py/grasp-agents)
|
25
|
+
[](https://mit-license.org/)
|
26
|
+
[](https://pypi.org/project/grasp-agents/)
|
27
|
+
[](https://github.com/grasp-technologies/grasp-agents/stargazers)
|
28
|
+
[](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=
|
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
|
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=
|
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.
|
42
|
-
grasp_agents-0.1.
|
43
|
-
grasp_agents-0.1.
|
44
|
-
grasp_agents-0.1.
|
41
|
+
grasp_agents-0.1.18.dist-info/METADATA,sha256=hQujnchtcu1AdgCKpyGxATI-Nw-UT_QU7tLQI4883FM,7004
|
42
|
+
grasp_agents-0.1.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
43
|
+
grasp_agents-0.1.18.dist-info/licenses/LICENSE.md,sha256=Kfeo0gdlLS6tLQiWwO9UWhjp9-f93a5kShSiBp2FG-c,1201
|
44
|
+
grasp_agents-0.1.18.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
|
-
[](https://badge.fury.io/py/grasp-agents)
|
25
|
-
[](https://mit-license.org/)
|
26
|
-
[](https://pypi.org/project/grasp-agents/)
|
27
|
-
[](https://github.com/grasp-technologies/grasp-agents/stargazers)
|
28
|
-
[](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
|
File without changes
|
File without changes
|
File without changes
|