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.
Files changed (82) hide show
  1. {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/tests.yaml +18 -8
  2. {iointel-1.3.2 → iointel-1.3.4}/.gitignore +5 -1
  3. {iointel-1.3.2 → iointel-1.3.4}/PKG-INFO +8 -6
  4. {iointel-1.3.2 → iointel-1.3.4}/README.md +4 -4
  5. iointel-1.3.4/docker-compose.yml +33 -0
  6. {iointel-1.3.2 → iointel-1.3.4}/iointel/__init__.py +1 -1
  7. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/tool_factory.py +6 -2
  8. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/data_models/datamodels.py +9 -7
  9. iointel-1.3.4/iointel/src/agent_methods/tools/agno/agentql.py +21 -0
  10. iointel-1.3.4/iointel/src/agent_methods/tools/agno/airflow.py +22 -0
  11. iointel-1.3.4/iointel/src/agent_methods/tools/agno/arxiv.py +27 -0
  12. iointel-1.3.4/iointel/src/agent_methods/tools/agno/aws_lambda.py +20 -0
  13. iointel-1.3.4/iointel/src/agent_methods/tools/agno/common.py +11 -0
  14. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/duckduckgo.py +13 -1
  15. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/chainables.py +8 -6
  16. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/decorators.py +1 -1
  17. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/workflow.py +10 -1
  18. {iointel-1.3.2 → iointel-1.3.4}/pyproject.toml +3 -1
  19. iointel-1.3.4/tests/conftest.py +0 -0
  20. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_basic_agent_tools.py +1 -1
  21. iointel-1.3.2/Makefile +0 -38
  22. {iointel-1.3.2 → iointel-1.3.4}/.gitattributes +0 -0
  23. {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/codeql.yaml +0 -0
  24. {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/linting.yaml +0 -0
  25. {iointel-1.3.2 → iointel-1.3.4}/.github/workflows/release.yaml +0 -0
  26. {iointel-1.3.2 → iointel-1.3.4}/.pre-commit-config.yaml +0 -0
  27. {iointel-1.3.2 → iointel-1.3.4}/LICENSE +0 -0
  28. {iointel-1.3.2 → iointel-1.3.4}/creds.env.example +0 -0
  29. {iointel-1.3.2 → iointel-1.3.4}/iointel/client/__init__.py +0 -0
  30. {iointel-1.3.2 → iointel-1.3.4}/iointel/client/client.py +0 -0
  31. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/__init__.py +0 -0
  32. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/__init__.py +0 -0
  33. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/__init__.py +0 -0
  34. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/agents/agents_factory.py +0 -0
  35. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/data_models/__init__.py +0 -0
  36. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/prompts/__init__.py +0 -0
  37. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/prompts/instructions.py +0 -0
  38. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/__init__.py +0 -0
  39. {iointel-1.3.2/iointel/src/code_parsers → iointel-1.3.4/iointel/src/agent_methods/tools/agno}/__init__.py +0 -0
  40. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/coinmarketcap.py +0 -0
  41. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/firecrawl.py +0 -0
  42. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/r2r.py +0 -0
  43. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/searxng.py +0 -0
  44. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/solscan.py +0 -0
  45. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/tools.py +0 -0
  46. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/utils.py +0 -0
  47. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agent_methods/tools/wolfram.py +0 -0
  48. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/agents.py +0 -0
  49. /iointel-1.3.2/tests/conftest.py → /iointel-1.3.4/iointel/src/code_parsers/__init__.py +0 -0
  50. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/code_parsers/jscode_parser.py +0 -0
  51. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/code_parsers/pycode_parser.py +0 -0
  52. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/memory.py +0 -0
  53. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/task.py +0 -0
  54. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/constants.py +0 -0
  55. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/exceptions.py +0 -0
  56. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/func_metadata.py +0 -0
  57. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/graph_nodes.py +0 -0
  58. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/helpers.py +0 -0
  59. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/registries.py +0 -0
  60. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/rich.py +0 -0
  61. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/runners.py +0 -0
  62. {iointel-1.3.2 → iointel-1.3.4}/iointel/src/utilities/stages.py +0 -0
  63. {iointel-1.3.2 → iointel-1.3.4}/test-runner +0 -0
  64. {iointel-1.3.2 → iointel-1.3.4}/tests/test_agents.py +0 -0
  65. {iointel-1.3.2 → iointel-1.3.4}/tests/test_client.py +0 -0
  66. {iointel-1.3.2 → iointel-1.3.4}/tests/test_stages.py +0 -0
  67. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tasks.py +0 -0
  68. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_coinmarketcap.py +0 -0
  69. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_duckduckgo.py +0 -0
  70. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_firecrawl_tool.py +0 -0
  71. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_r2r.py +0 -0
  72. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_searxng.py +0 -0
  73. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_solscan.py +0 -0
  74. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_isolated/test_wolfram.py +0 -0
  75. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_coinmarketcap_agent.py +0 -0
  76. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_crawl_agent.py +0 -0
  77. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_duckduckgo_agent.py +0 -0
  78. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_searxng_agent.py +0 -0
  79. {iointel-1.3.2 → iointel-1.3.4}/tests/test_tools_with_agents/test_wolframalpha_agent.py +0 -0
  80. {iointel-1.3.2 → iointel-1.3.4}/tests/test_utilities.py +0 -0
  81. {iointel-1.3.2 → iointel-1.3.4}/tests/test_workflows.py +0 -0
  82. {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: Prepare for tests
63
- run: make prepare_for_tests
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: Check readme anchors
70
- run: |
71
- pip install pipx
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
@@ -11,4 +11,8 @@ dist
11
11
  .idea
12
12
 
13
13
  *.ipynb
14
- *.ipynb_checkpoints
14
+ *.ipynb_checkpoints
15
+
16
+ # virtual environments
17
+ venv
18
+ .venv
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iointel
3
- Version: 1.3.2
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: duckduckgo-search==8.0.0
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(text="...").translate_text().sentiment().run_tasks()`).
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(text="This is the text to analyze", client_mode=False)
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(text="Breaking news: local sports team wins!", client_mode=False)
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(text="Breaking news: local sports team wins!", client_mode=True)
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(text="...").translate_text().sentiment().run_tasks()`).
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(text="This is the text to analyze", client_mode=False)
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(text="Breaking news: local sports team wins!", client_mode=False)
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(text="Breaking news: local sports team wins!", client_mode=True)
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
@@ -45,4 +45,4 @@ __all__ = [
45
45
  ]
46
46
 
47
47
 
48
- __version__ = "1.3.2"
48
+ __version__ = "1.3.4"
@@ -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, dict):
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 = "auto"
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("Sexually explicit content detected")
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(text="Some input text", client_mode=False, agents=[swarm])
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.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