iointel 1.3.2__tar.gz → 1.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/tests.yaml +18 -8
- {iointel-1.3.2 → iointel-1.3.4}/.gitignore +5 -1
- {iointel-1.3.2 → iointel-1.3.4}/PKG-INFO +8 -6
- {iointel-1.3.2 → iointel-1.3.4}/README.md +4 -4
- iointel-1.3.4/docker-compose.yml +33 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/__init__.py +1 -1
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/tool_factory.py +6 -2
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/data_models/datamodels.py +9 -7
- iointel-1.3.4/iointel/src/agent_methods/tools/agno/agentql.py +21 -0
- iointel-1.3.4/iointel/src/agent_methods/tools/agno/airflow.py +22 -0
- iointel-1.3.4/iointel/src/agent_methods/tools/agno/arxiv.py +27 -0
- iointel-1.3.4/iointel/src/agent_methods/tools/agno/aws_lambda.py +20 -0
- iointel-1.3.4/iointel/src/agent_methods/tools/agno/common.py +11 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/duckduckgo.py +13 -1
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/chainables.py +8 -6
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/decorators.py +1 -1
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/workflow.py +10 -1
- {iointel-1.3.2 → iointel-1.3.4}/pyproject.toml +3 -1
- iointel-1.3.4/tests/conftest.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_basic_agent_tools.py +1 -1
- iointel-1.3.2/Makefile +0 -38
- {iointel-1.3.2 → iointel-1.3.4}/.gitattributes +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/codeql.yaml +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/linting.yaml +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/release.yaml +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/.pre-commit-config.yaml +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/LICENSE +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/creds.env.example +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/client/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/client/client.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/agents_factory.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/data_models/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/prompts/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/prompts/instructions.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/__init__.py +0 -0
- {iointel-1.3.2/iointel/src/code_parsers → iointel-1.3.4/iointel/src/agent_methods/tools/agno}/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/coinmarketcap.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/firecrawl.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/r2r.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/searxng.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/solscan.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/tools.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/utils.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/wolfram.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agents.py +0 -0
- /iointel-1.3.2/tests/conftest.py → /iointel-1.3.4/iointel/src/code_parsers/__init__.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/code_parsers/jscode_parser.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/code_parsers/pycode_parser.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/memory.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/task.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/constants.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/exceptions.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/func_metadata.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/graph_nodes.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/helpers.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/registries.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/rich.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/runners.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/stages.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/test-runner +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_agents.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_client.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_stages.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tasks.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_coinmarketcap.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_duckduckgo.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_firecrawl_tool.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_r2r.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_searxng.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_solscan.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_wolfram.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_coinmarketcap_agent.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_crawl_agent.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_duckduckgo_agent.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_searxng_agent.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_wolframalpha_agent.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_utilities.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/tests/test_workflows.py +0 -0
- {iointel-1.3.2 → iointel-1.3.4}/uv.lock +0 -0
|
@@ -52,21 +52,31 @@ jobs:
|
|
|
52
52
|
uses: actions/setup-python@v5
|
|
53
53
|
with:
|
|
54
54
|
python-version: ${{ matrix.python-version }}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
- name: download uv
|
|
57
57
|
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
- name: Install dependencies
|
|
60
60
|
run: uv pip install --system -e ".[dev]"
|
|
61
|
+
|
|
62
|
+
- name: Check readme anchors
|
|
63
|
+
run: |
|
|
64
|
+
pip install pipx
|
|
65
|
+
pipx run hatch-fancy-pypi-readme | grep -vq 'MISSING ANCHOR'
|
|
66
|
+
|
|
67
|
+
- name: Set up Docker Compose
|
|
68
|
+
uses: docker/setup-buildx-action@v3
|
|
61
69
|
|
|
62
|
-
- name:
|
|
63
|
-
run:
|
|
70
|
+
- name: Start services
|
|
71
|
+
run: |
|
|
72
|
+
docker compose up -d
|
|
73
|
+
# Wait for searxng to be healthy by checking the health endpoint directly
|
|
74
|
+
timeout 60s bash -c 'until curl -s -f http://localhost:8080/healthz > /dev/null; do sleep 2; done'
|
|
64
75
|
|
|
65
76
|
- name: Run tests
|
|
66
77
|
run: pytest -vv tests/ --retries 2 --retry-delay 5
|
|
67
78
|
if: ${{ !(github.event.pull_request.head.repo.fork) }}
|
|
68
79
|
|
|
69
|
-
- name:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
pipx run hatch-fancy-pypi-readme | grep -vq 'MISSING ANCHOR'
|
|
80
|
+
- name: Remove containers
|
|
81
|
+
if: always() # This ensures cleanup happens even if tests fail
|
|
82
|
+
run: docker compose down -v
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iointel
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: A framework to create agents, tasks, and workflows.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: AI,Agents,LLM,framework,tools
|
|
7
7
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
|
-
Requires-Dist:
|
|
9
|
+
Requires-Dist: agno[tools]==1.5.5
|
|
10
|
+
Requires-Dist: backoff==2.2.1
|
|
11
|
+
Requires-Dist: duckduckgo-search==8.0.2
|
|
10
12
|
Requires-Dist: firecrawl-py>=2.2.0
|
|
11
13
|
Requires-Dist: firecrawl>=2.2.0
|
|
12
14
|
Requires-Dist: httpx
|
|
@@ -117,7 +119,7 @@ Users can define tasks (like `sentiment`, `translate_text`, etc.) in a **local**
|
|
|
117
119
|
|
|
118
120
|
- A **task** is a single step in a workflow, e.g., `schedule_reminder`, `sentiment`, `translate_text`, etc.
|
|
119
121
|
- Tasks are managed by the `Workflow` class in `workflow.py`.
|
|
120
|
-
- Tasks can be chained for multi-step logic into a workflow (e.g., `await Workflow(
|
|
122
|
+
- Tasks can be chained for multi-step logic into a workflow (e.g., `await Workflow(objective="...").translate_text().sentiment().run_tasks()`).
|
|
121
123
|
|
|
122
124
|
### Client Mode vs Local Mode<a id="client-mode-vs-local-mode"></a>
|
|
123
125
|
|
|
@@ -199,7 +201,7 @@ In Python code, you can create tasks by instantiating the Tasks class and chaini
|
|
|
199
201
|
```python
|
|
200
202
|
from iointel import Workflow
|
|
201
203
|
|
|
202
|
-
tasks = Workflow(
|
|
204
|
+
tasks = Workflow(objective="This is the text to analyze", client_mode=False)
|
|
203
205
|
(
|
|
204
206
|
tasks
|
|
205
207
|
.sentiment(agents=[my_agent])
|
|
@@ -214,14 +216,14 @@ Because client_mode=False, everything runs locally.
|
|
|
214
216
|
### Running a Local Workflow<a id="running-a-local-workflow"></a>
|
|
215
217
|
|
|
216
218
|
```python
|
|
217
|
-
tasks = Workflow(
|
|
219
|
+
tasks = Workflow(objective="Breaking news: local sports team wins!", client_mode=False)
|
|
218
220
|
await tasks.summarize_text(max_words=50).run_tasks()
|
|
219
221
|
```
|
|
220
222
|
|
|
221
223
|
### Running a Remote Workflow (Client Mode)<a id="running-a-remote-workflow-client-mode"></a>
|
|
222
224
|
|
|
223
225
|
```python
|
|
224
|
-
tasks = Workflow(
|
|
226
|
+
tasks = Workflow(objective="Breaking news: local sports team wins!", client_mode=True)
|
|
225
227
|
await tasks.summarize_text(max_words=50).run_tasks()
|
|
226
228
|
```
|
|
227
229
|
Now, summarize_text calls the client function (e.g., summarize_task(...)) instead of local logic.
|
|
@@ -84,7 +84,7 @@ Users can define tasks (like `sentiment`, `translate_text`, etc.) in a **local**
|
|
|
84
84
|
|
|
85
85
|
- A **task** is a single step in a workflow, e.g., `schedule_reminder`, `sentiment`, `translate_text`, etc.
|
|
86
86
|
- Tasks are managed by the `Workflow` class in `workflow.py`.
|
|
87
|
-
- Tasks can be chained for multi-step logic into a workflow (e.g., `await Workflow(
|
|
87
|
+
- Tasks can be chained for multi-step logic into a workflow (e.g., `await Workflow(objective="...").translate_text().sentiment().run_tasks()`).
|
|
88
88
|
|
|
89
89
|
### Client Mode vs Local Mode<a id="client-mode-vs-local-mode"></a>
|
|
90
90
|
|
|
@@ -166,7 +166,7 @@ In Python code, you can create tasks by instantiating the Tasks class and chaini
|
|
|
166
166
|
```python
|
|
167
167
|
from iointel import Workflow
|
|
168
168
|
|
|
169
|
-
tasks = Workflow(
|
|
169
|
+
tasks = Workflow(objective="This is the text to analyze", client_mode=False)
|
|
170
170
|
(
|
|
171
171
|
tasks
|
|
172
172
|
.sentiment(agents=[my_agent])
|
|
@@ -181,14 +181,14 @@ Because client_mode=False, everything runs locally.
|
|
|
181
181
|
### Running a Local Workflow<a id="running-a-local-workflow"></a>
|
|
182
182
|
|
|
183
183
|
```python
|
|
184
|
-
tasks = Workflow(
|
|
184
|
+
tasks = Workflow(objective="Breaking news: local sports team wins!", client_mode=False)
|
|
185
185
|
await tasks.summarize_text(max_words=50).run_tasks()
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
### Running a Remote Workflow (Client Mode)<a id="running-a-remote-workflow-client-mode"></a>
|
|
189
189
|
|
|
190
190
|
```python
|
|
191
|
-
tasks = Workflow(
|
|
191
|
+
tasks = Workflow(objective="Breaking news: local sports team wins!", client_mode=True)
|
|
192
192
|
await tasks.summarize_text(max_words=50).run_tasks()
|
|
193
193
|
```
|
|
194
194
|
Now, summarize_text calls the client function (e.g., summarize_task(...)) instead of local logic.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
searxng:
|
|
5
|
+
image: searxng/searxng
|
|
6
|
+
container_name: iointel-searxng
|
|
7
|
+
ports:
|
|
8
|
+
- "8080:8080"
|
|
9
|
+
volumes:
|
|
10
|
+
- searxng_config:/etc/searxng
|
|
11
|
+
environment:
|
|
12
|
+
- BASE_URL=http://localhost:8080/
|
|
13
|
+
- INSTANCE_NAME=my-instance
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
|
|
16
|
+
interval: 30s
|
|
17
|
+
timeout: 10s
|
|
18
|
+
retries: 3
|
|
19
|
+
start_period: 5s
|
|
20
|
+
restart: unless-stopped
|
|
21
|
+
|
|
22
|
+
# Service to modify settings.yml
|
|
23
|
+
setup:
|
|
24
|
+
image: python:3-slim
|
|
25
|
+
volumes:
|
|
26
|
+
- searxng_config:/etc/searxng
|
|
27
|
+
command: ["bash", "-c", "pip install pyyaml && python3 -c 'import yaml; f=\"/etc/searxng/settings.yml\"; d=yaml.safe_load(open(f)); d.setdefault(\"search\",{}).setdefault(\"formats\",[]).append(\"json\"); open(f,\"w\").write(yaml.safe_dump(d))'"]
|
|
28
|
+
depends_on:
|
|
29
|
+
- searxng
|
|
30
|
+
|
|
31
|
+
volumes:
|
|
32
|
+
searxng_config:
|
|
33
|
+
name: iointel-searxng-config
|
|
@@ -63,7 +63,11 @@ def resolve_tools(params: AgentParams) -> List[Optional[Callable]]:
|
|
|
63
63
|
"""
|
|
64
64
|
resolved_tools = []
|
|
65
65
|
for tool_data in params.tools:
|
|
66
|
-
if isinstance(tool_data,
|
|
66
|
+
if isinstance(tool_data, str):
|
|
67
|
+
logger.debug(f"Looking up the registry for tool `{tool_data}`")
|
|
68
|
+
if not (tool_obj := TOOLS_REGISTRY.get(tool_data)):
|
|
69
|
+
raise ValueError(f"Tool {tool_data} is not known")
|
|
70
|
+
elif isinstance(tool_data, dict):
|
|
67
71
|
logger.debug(f"Rehydrating tool from dict: {tool_data}")
|
|
68
72
|
tool_obj = Tool.model_validate(tool_data)
|
|
69
73
|
if "body" in tool_data:
|
|
@@ -82,7 +86,7 @@ def resolve_tools(params: AgentParams) -> List[Optional[Callable]]:
|
|
|
82
86
|
tool_obj = Tool.from_function(tool_data)
|
|
83
87
|
else:
|
|
84
88
|
raise ValueError(
|
|
85
|
-
"Unexpected type for tool_data; expected dict, Tool instance, or callable."
|
|
89
|
+
"Unexpected type for tool_data; expected str, dict, Tool instance, or callable."
|
|
86
90
|
)
|
|
87
91
|
|
|
88
92
|
registered_tool_name, registered_tool = next(
|
|
@@ -400,7 +400,7 @@ class AgentParams(BaseModel):
|
|
|
400
400
|
base_url: Optional[str] = Field(
|
|
401
401
|
None, description="Base URL for the model, if required."
|
|
402
402
|
)
|
|
403
|
-
tools: Optional[List[dict | Tool | Callable]] = Field(default_factory=list)
|
|
403
|
+
tools: Optional[List[str | dict | Tool | Callable]] = Field(default_factory=list)
|
|
404
404
|
context: Optional[Dict[str, Any]] = Field(
|
|
405
405
|
None,
|
|
406
406
|
description="Context to be passed to the agent.",
|
|
@@ -446,12 +446,6 @@ class TranslationResult(BaseModel):
|
|
|
446
446
|
Activation = Annotated[float, Field(ge=0, le=1)]
|
|
447
447
|
|
|
448
448
|
|
|
449
|
-
class ModerationException(Exception):
|
|
450
|
-
"""Exception raised when a message is not allowed."""
|
|
451
|
-
|
|
452
|
-
...
|
|
453
|
-
|
|
454
|
-
|
|
455
449
|
class ViolationActivation(TypedDict):
|
|
456
450
|
"""Violation activation."""
|
|
457
451
|
|
|
@@ -463,6 +457,14 @@ class ViolationActivation(TypedDict):
|
|
|
463
457
|
dangerous_content: Activation
|
|
464
458
|
|
|
465
459
|
|
|
460
|
+
class ModerationException(Exception):
|
|
461
|
+
"""Exception raised when a message is not allowed."""
|
|
462
|
+
|
|
463
|
+
def __init__(self, *args, violations: ViolationActivation):
|
|
464
|
+
super().__init__(*args)
|
|
465
|
+
self.violations = violations
|
|
466
|
+
|
|
467
|
+
|
|
466
468
|
##### task and workflow models ########
|
|
467
469
|
class BaseStage(BaseModel):
|
|
468
470
|
stage_id: Optional[int] = None
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from agno.tools.agentql import AgentQLTools as AgnoAgentQLTools
|
|
4
|
+
|
|
5
|
+
from ..utils import register_tool
|
|
6
|
+
from .common import DisableAgnoRegistryMixin
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentQL(DisableAgnoRegistryMixin, AgnoAgentQLTools):
|
|
10
|
+
def __init__(self, api_key: Optional[str] = None, agentql_query: str = ""):
|
|
11
|
+
super().__init__(api_key=api_key, scrape=True, agentql_query=agentql_query)
|
|
12
|
+
|
|
13
|
+
@register_tool(name="agentql_scrape_website")
|
|
14
|
+
@wraps(AgnoAgentQLTools.scrape_website)
|
|
15
|
+
def scrape_website(self, url: str) -> str:
|
|
16
|
+
return super().scrape_website(url)
|
|
17
|
+
|
|
18
|
+
@register_tool(name="agentql_custom_scrape_website")
|
|
19
|
+
@wraps(AgnoAgentQLTools.custom_scrape_website)
|
|
20
|
+
def custom_scrape_website(self, url: str) -> str:
|
|
21
|
+
return super().custom_scrape_website(url)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from agno.tools.airflow import AirflowTools as AgnoAirflowTools
|
|
5
|
+
|
|
6
|
+
from ..utils import register_tool
|
|
7
|
+
from .common import DisableAgnoRegistryMixin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Airflow(DisableAgnoRegistryMixin, AgnoAirflowTools):
|
|
11
|
+
def __init__(self, dags_dir: Optional[Union[Path, str]] = None):
|
|
12
|
+
super().__init__(dags_dir=dags_dir, save_dag=True, read_dag=True)
|
|
13
|
+
|
|
14
|
+
@register_tool(name="airflow_save_dag_file")
|
|
15
|
+
@wraps(AgnoAirflowTools.save_dag_file)
|
|
16
|
+
def save_dag_file(self, contents: str, dag_file: str) -> str:
|
|
17
|
+
return super().save_dag_file(contents=contents, dag_file=dag_file)
|
|
18
|
+
|
|
19
|
+
@register_tool(name="airflow_read_dag_file")
|
|
20
|
+
@wraps(AgnoAirflowTools.read_dag_file)
|
|
21
|
+
def read_dag_file(self, dag_file: str) -> str:
|
|
22
|
+
return super().read_dag_file(dag_file=dag_file)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from agno.tools.arxiv import ArxivTools as AgnoArxivTools
|
|
5
|
+
|
|
6
|
+
from ..utils import register_tool
|
|
7
|
+
from .common import DisableAgnoRegistryMixin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Arxiv(DisableAgnoRegistryMixin, AgnoArxivTools):
|
|
11
|
+
def __init__(self, download_dir: Optional[Path] = None):
|
|
12
|
+
# TODO: define a better default option for downloading pdfs, agno arxiv has a weird choice
|
|
13
|
+
super().__init__(download_dir=download_dir)
|
|
14
|
+
|
|
15
|
+
@register_tool(name="arxiv_search")
|
|
16
|
+
@wraps(AgnoArxivTools.search_arxiv_and_return_articles)
|
|
17
|
+
def search_arxiv_and_return_articles(
|
|
18
|
+
self, query: str, num_articles: int = 10
|
|
19
|
+
) -> str:
|
|
20
|
+
return super().search_arxiv_and_return_articles(query, num_articles)
|
|
21
|
+
|
|
22
|
+
@register_tool(name="arxiv_read_papers")
|
|
23
|
+
@wraps(AgnoArxivTools.read_arxiv_papers)
|
|
24
|
+
def read_arxiv_papers(
|
|
25
|
+
self, id_list: list[str], pages_to_read: Optional[int] = None
|
|
26
|
+
) -> str:
|
|
27
|
+
return super().read_arxiv_papers(id_list, pages_to_read)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from agno.tools.aws_lambda import AWSLambdaTools as AgnoAWSLambdaTools
|
|
3
|
+
|
|
4
|
+
from ..utils import register_tool
|
|
5
|
+
from .common import DisableAgnoRegistryMixin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AWSLambda(DisableAgnoRegistryMixin, AgnoAWSLambdaTools):
|
|
9
|
+
def __init__(self, region_name: str = "us-east-1"):
|
|
10
|
+
super().__init__(region_name=region_name)
|
|
11
|
+
|
|
12
|
+
@register_tool(name="aws_lambda_list_functions")
|
|
13
|
+
@wraps(AgnoAWSLambdaTools.list_functions)
|
|
14
|
+
def list_functions(self) -> str:
|
|
15
|
+
return super().list_functions()
|
|
16
|
+
|
|
17
|
+
@register_tool(name="aws_lambda_invoke_function")
|
|
18
|
+
@wraps(AgnoAWSLambdaTools.invoke_function)
|
|
19
|
+
def invoke_function(self, function_name: str, payload: str = "{}") -> str:
|
|
20
|
+
return super().invoke_function(function_name=function_name, payload=payload)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class DisableAgnoRegistryMixin:
|
|
2
|
+
"""
|
|
3
|
+
Put this as first parent class when inheriting
|
|
4
|
+
from Agno tool to disable Agno registry,
|
|
5
|
+
because we only care about our own registry."""
|
|
6
|
+
|
|
7
|
+
def _register_tools(self):
|
|
8
|
+
"""Disabled in favour of iointel registry."""
|
|
9
|
+
|
|
10
|
+
def register(self, function, name=None):
|
|
11
|
+
"""Disabled in favour of iointel registry."""
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from typing import Any, Dict, List, Optional
|
|
3
|
+
import backoff
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from duckduckgo import DuckDuckGoSearchException
|
|
7
|
+
except ImportError:
|
|
8
|
+
DuckDuckGoSearchException = Exception
|
|
3
9
|
|
|
4
10
|
from iointel.src.utilities.decorators import register_tool
|
|
5
11
|
from pydantic import BaseModel, ConfigDict, model_validator
|
|
@@ -24,7 +30,7 @@ class DuckDuckGoSearchAPIWrapper(BaseModel):
|
|
|
24
30
|
Options: d, w, m, y
|
|
25
31
|
"""
|
|
26
32
|
max_results: int = 5
|
|
27
|
-
backend: str = "
|
|
33
|
+
backend: str = "html"
|
|
28
34
|
"""
|
|
29
35
|
Options: auto, html, lite
|
|
30
36
|
"""
|
|
@@ -46,6 +52,12 @@ class DuckDuckGoSearchAPIWrapper(BaseModel):
|
|
|
46
52
|
) from e
|
|
47
53
|
return values
|
|
48
54
|
|
|
55
|
+
@backoff.on_exception(
|
|
56
|
+
backoff.expo,
|
|
57
|
+
exception=(DuckDuckGoSearchException,),
|
|
58
|
+
max_tries=5,
|
|
59
|
+
jitter=backoff.random_jitter,
|
|
60
|
+
)
|
|
49
61
|
def _ddgs(
|
|
50
62
|
self, query: str, max_results: Optional[int] = None
|
|
51
63
|
) -> List[Dict[str, str]]:
|
|
@@ -225,17 +225,19 @@ async def execute_moderation(
|
|
|
225
225
|
).execute()
|
|
226
226
|
|
|
227
227
|
if result["extreme_profanity"] > threshold:
|
|
228
|
-
raise ModerationException("Extreme profanity detected")
|
|
228
|
+
raise ModerationException("Extreme profanity detected", violations=result)
|
|
229
229
|
elif result["sexually_explicit"] > threshold:
|
|
230
|
-
raise ModerationException(
|
|
230
|
+
raise ModerationException(
|
|
231
|
+
"Sexually explicit content detected", violations=result
|
|
232
|
+
)
|
|
231
233
|
elif result["hate_speech"] > threshold:
|
|
232
|
-
raise ModerationException("Hate speech detected")
|
|
234
|
+
raise ModerationException("Hate speech detected", violations=result)
|
|
233
235
|
elif result["harassment"] > threshold:
|
|
234
|
-
raise ModerationException("Harassment detected")
|
|
236
|
+
raise ModerationException("Harassment detected", violations=result)
|
|
235
237
|
elif result["self_harm"] > threshold:
|
|
236
|
-
raise ModerationException("Self harm detected")
|
|
238
|
+
raise ModerationException("Self harm detected", violations=result)
|
|
237
239
|
elif result["dangerous_content"] > threshold:
|
|
238
|
-
raise ModerationException("Dangeme profanity detected")
|
|
240
|
+
raise ModerationException("Dangeme profanity detected", violations=result)
|
|
239
241
|
|
|
240
242
|
return result
|
|
241
243
|
|
|
@@ -97,7 +97,7 @@ def register_tool(_fn=None, name: Optional[str] = None):
|
|
|
97
97
|
logger.debug(f"Tool '{tool_name}' is already safely registered.")
|
|
98
98
|
return executor_fn
|
|
99
99
|
|
|
100
|
-
TOOLS_REGISTRY[tool_name] = Tool.from_function(executor_fn)
|
|
100
|
+
TOOLS_REGISTRY[tool_name] = Tool.from_function(executor_fn, name=tool_name)
|
|
101
101
|
logger.debug(f"Registered tool '{tool_name}' safely.")
|
|
102
102
|
return executor_fn
|
|
103
103
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import List, Optional, Any
|
|
2
2
|
import uuid
|
|
3
3
|
import inspect
|
|
4
|
+
import warnings
|
|
4
5
|
import yaml
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from collections import defaultdict
|
|
@@ -59,7 +60,7 @@ class Workflow:
|
|
|
59
60
|
Manages a chain of tasks and runs them sequentially.
|
|
60
61
|
|
|
61
62
|
Example usage:
|
|
62
|
-
workflow = Workflow(
|
|
63
|
+
workflow = Workflow(objective="Some input text", client_mode=False, agents=[swarm])
|
|
63
64
|
workflow.summarize_text(max_words=50).custom(name="do-fancy-thing", objective="Fancy step", agents=[my_agent])
|
|
64
65
|
results = await workflow.run_tasks()
|
|
65
66
|
"""
|
|
@@ -67,9 +68,17 @@ class Workflow:
|
|
|
67
68
|
def __init__(
|
|
68
69
|
self,
|
|
69
70
|
objective: str = "",
|
|
71
|
+
text: str | None = None,
|
|
70
72
|
client_mode: bool = True,
|
|
71
73
|
agents: Optional[List[Any]] = None,
|
|
72
74
|
):
|
|
75
|
+
if text is not None:
|
|
76
|
+
if objective:
|
|
77
|
+
raise ValueError("Both `text` and `objective` parameters set")
|
|
78
|
+
objective = text
|
|
79
|
+
warnings.warn(
|
|
80
|
+
"`text` parameter is deprecated, please use `objective` instead"
|
|
81
|
+
)
|
|
73
82
|
self.tasks: List[dict] = []
|
|
74
83
|
self.objective = objective
|
|
75
84
|
self.client_mode = client_mode
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"python-multipart>=0.0.20",
|
|
11
11
|
"pydantic-ai>=0.1.0",
|
|
12
12
|
"pyyaml>=6.0.2",
|
|
13
|
-
"duckduckgo-search==8.0.
|
|
13
|
+
"duckduckgo-search==8.0.2",
|
|
14
14
|
"pgvector>=0.4.0",
|
|
15
15
|
"sqlalchemy-utils>=0.41.2",
|
|
16
16
|
"sqlalchemy>=2.0.36",
|
|
@@ -18,6 +18,8 @@ dependencies = [
|
|
|
18
18
|
# For some reason both firecrawl-py and firecrawl are necessary
|
|
19
19
|
"firecrawl-py>=2.2.0",
|
|
20
20
|
"firecrawl>=2.2.0",
|
|
21
|
+
"backoff==2.2.1",
|
|
22
|
+
"agno[tools]==1.5.5",
|
|
21
23
|
]
|
|
22
24
|
keywords = ["Agents", "LLM", "framework", "AI", "tools"]
|
|
23
25
|
classifiers = [
|
|
File without changes
|
|
@@ -34,7 +34,7 @@ async def test_basic_tools():
|
|
|
34
34
|
Complete tasks to the best of your ability by using the appropriate tool. Follow all instructions carefully.
|
|
35
35
|
When you need to add numbers, call the tool and use its result.
|
|
36
36
|
""",
|
|
37
|
-
tools=[add_two_numbers, get_current_datetime],
|
|
37
|
+
tools=["add_two_numbers", get_current_datetime],
|
|
38
38
|
)
|
|
39
39
|
numbers = [22122837493142, 159864395786239452]
|
|
40
40
|
|
iointel-1.3.2/Makefile
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
prepare_for_tests:
|
|
2
|
-
export PORT=8080
|
|
3
|
-
docker pull searxng/searxng
|
|
4
|
-
docker stop iointel-searxng || true
|
|
5
|
-
|
|
6
|
-
# Run searxng to generate default settings.yml
|
|
7
|
-
# Then edit settings.yml to support json format in the output, and restart docker with updated settings
|
|
8
|
-
docker run --rm -d \
|
|
9
|
-
-p ${PORT}:8080 \
|
|
10
|
-
-v "${PWD}/searxng:/etc/searxng" \
|
|
11
|
-
-e "BASE_URL=http://localhost:${PORT}/" \
|
|
12
|
-
-e "INSTANCE_NAME=my-instance" \
|
|
13
|
-
--name iointel-searxng \
|
|
14
|
-
searxng/searxng
|
|
15
|
-
sleep 5
|
|
16
|
-
docker stop iointel-searxng || true
|
|
17
|
-
|
|
18
|
-
sudo python3 -c "import yaml; f='searxng/settings.yml'; d=yaml.safe_load(open(f)); d.setdefault('search',{}).setdefault('formats',[]).append('json'); open(f,'w').write(yaml.safe_dump(d))"
|
|
19
|
-
|
|
20
|
-
docker run --rm -d \
|
|
21
|
-
-p ${PORT}:8080 \
|
|
22
|
-
-v "${PWD}/searxng:/etc/searxng" \
|
|
23
|
-
-e "BASE_URL=http://localhost:${PORT}/" \
|
|
24
|
-
-e "INSTANCE_NAME=my-instance" \
|
|
25
|
-
--name iointel-searxng \
|
|
26
|
-
searxng/searxng
|
|
27
|
-
sleep 5
|
|
28
|
-
# # Retrieve the response from localhost:8080 silently.
|
|
29
|
-
# @response=$$(curl -s localhost:8080) ; \
|
|
30
|
-
# if [ -n "$$response" ]; then \
|
|
31
|
-
# echo "Success: Server returned output."; \
|
|
32
|
-
# else \
|
|
33
|
-
# echo "Error: No output received from server."; \
|
|
34
|
-
# exit 1; \
|
|
35
|
-
# fi
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.PHONY: prepare_for_tests
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|