Compare commits

..

98 Commits

Author SHA1 Message Date
Eduardo Chiarotti
835f0065f1 test: change tests and gh action file 2024-05-16 21:15:20 -03:00
Eduardo Chiarotti
5865a2b899 fix: removed fix since it didnt changed the test 2024-05-16 21:07:39 -03:00
Eduardo Chiarotti
9629337f17 fix: fix test 2024-05-16 21:04:15 -03:00
Eduardo Chiarotti
2186f5c968 fix: test.yml 2024-05-16 20:49:58 -03:00
Eduardo Chiarotti
d34c2a2672 fix: fix typing hinting issue on code 2024-05-16 19:48:42 -03:00
Eduardo Chiarotti
2520f389f2 feat: add the tests 2024-05-16 19:43:42 -03:00
Eduardo Chiarotti
a958b31768 feat: add crewai train CLI command 2024-05-16 19:43:33 -03:00
Eduardo Chiarotti
5de494c99b fix: fix crewai-tools cli command 2024-05-15 19:54:26 -03:00
Jason Schrader
208c3a780c Add version command to CLI (#348)
* feat: add version command to cli with tools flag

* test: check output of version and tools flag

* fix: add version tool info to cli outputs
2024-05-15 19:50:49 -03:00
João Moura
1e112fa50a fixing crew base 2024-05-14 17:40:38 -03:00
João Moura
38fc5510ed ppreparing new version 0.30.9 2024-05-14 11:32:05 -03:00
João Moura
1a1f4717aa cutting new version with no yaml parsing 2024-05-13 23:09:29 -03:00
João Moura
977c6114ba preparing new version 2024-05-13 22:32:24 -03:00
João Moura
27fddae286 New version, updating dependencies, fixing memory 2024-05-13 22:26:41 -03:00
João Moura
615ac7f297 preparing new version 2024-05-13 12:59:55 -03:00
João Moura
87d28e896d preparing new version 2024-05-13 02:35:46 -03:00
Saif Mahmud
23f10418d7 Fixes #603 (#604) 2024-05-13 02:34:52 -03:00
João Moura
27e7f48a44 Adding new tests 2024-05-13 02:34:33 -03:00
João Moura
7fd8850ddb Small RC Fixes (#608)
* mentioning ollama on the docs as embedder

* lowering barrier to match tool with simialr name

* Fixing agent tools to support co_worker

* Adding new tests

* Fixing type"

* updating tests

* fixing conflict
2024-05-13 02:29:04 -03:00
Ítalo Vieira
7a4d3dd496 fix typo exectue -> execute (#607) 2024-05-13 02:19:06 -03:00
João Moura
c1d7936689 preparing new version 2024-05-12 19:56:40 -03:00
Eduardo Chiarotti
1ec4da6947 feat: add mypy as type checker, update code and add comment to reference (#591)
* fix: fix test actually running

* fix: fix test to not send request to openai

* fix: fix linting to remove cli files

* fix: exclude only files that breaks black

* fix: Fix all Ruff checkings on the code and Fix Test with repeated name

* fix: Change linter name on yml file

* feat: update pre-commit

* feat: remove need for isort on the code

* feat: add mypy as type checker, update code and add comment to reference

* feat: remove black linter

* feat: remove poetry to run the command

* feat: change logic to test mypy

* feat: update tests yml to try to fix the tests gh action

* feat: try to add just mypy to run on gh action

* feat: fix yml file

* feat: add comment to avoid issue on gh action

* feat: decouple pytest from the necessity of poetry install

* feat: change tests.yml to test different approach

* feat: change to poetry run

* fix: parameter field on yml file

* fix: update parameters to be on the pyproject

* fix: update pyproject to remove import untyped errors
2024-05-10 16:37:52 -03:00
Steven Edwards
8430c2f9af Task needs an expected_output field in docs. (#568)
* Task needs an expected_output field in docs..

* Add missing comma.
2024-05-10 11:55:10 -03:00
Ayo Ayibiowu
7cc6bccdec feat: adds support to automatically fallback to the default encoding (#596)
* feat: adds support to automatically fallbackk to the default encoding

* fix: use the correct method
2024-05-10 11:54:45 -03:00
Eduardo Chiarotti
aeba64feaf Feat: Add Ruff to improve linting/formatting (#588)
* fix: fix test actually running

* fix: fix test to not send request to openai

* fix: fix linting to remove cli files

* fix: exclude only files that breaks black

* fix: Fix all Ruff checkings on the code and Fix Test with repeated name

* fix: Change linter name on yml file

* feat: update pre-commit

* feat: remove need for isort on the code

* feat: remove black linter

* feat: update tests yml to try to fix the tests gh action
2024-05-10 11:53:53 -03:00
GabeKoga
04b4191de5 Fix/yaml formatting (#590)
* Bug/curly_braces_yaml

Added parser to help users on yaml syntax

* context error

Patch and later will prioritize this again to have context work with the yaml
2024-05-09 21:35:21 -03:00
Eduardo Chiarotti
1da7473f26 fix: fix test actually running (#587)
* fix: fix test actually running

* fix: fix test to not send request to openai

* fix: fix linting to remove cli files

* fix: exclude only files that breaks black
2024-05-09 21:33:48 -03:00
João Moura
95d13bd033 prepping new version 2024-05-09 09:12:57 -03:00
Eduardo Chiarotti
7eb4fcdaf4 fix: Add validation fix output_file issue when have '/' (#585)
* fix: Add validation fix output_file issue when have /

* fix: run black to format code

* fix: run black to format code
2024-05-09 08:11:00 -03:00
João Moura
809b4b227c Revert "Fix .md doc file 404 error on github (#564)" (#567)
This reverts commit 2bd30af72b.
2024-05-05 10:35:46 -03:00
Alex Fazio
ff51a2da9b corrected imprecision in the instantiation (#555) 2024-05-05 03:55:13 -03:00
João Moura
be83681665 preparing new RC version 2024-05-05 02:57:29 -03:00
Jackie Qi
2bd30af72b Fix .md doc file 404 error on github (#564)
* fix md file link not working on github

* miss one changed file
2024-05-05 02:53:20 -03:00
João Moura
d7b021061b updating .gitignore 2024-05-05 02:52:43 -03:00
João Moura
73647f1669 TYPO 2024-05-05 02:14:49 -03:00
João Moura
d341cb3d5c Fixing manager_agent_support 2024-05-05 00:51:18 -03:00
João Moura
30438410d6 cutting new RC 2024-05-03 00:55:32 -03:00
João Moura
b264ebabc0 adding meomization to crewai project annotations 2024-05-03 00:49:37 -03:00
tarekadam
2edc88e0a1 Update LLM-Connections.md (#553)
fixes command to lower case
2024-05-03 00:25:03 -03:00
João Moura
552dda46f8 updating manager llm pydantic error 2024-05-02 23:39:56 -03:00
João Moura
2340a127d6 curring new rc 2024-05-02 23:22:02 -03:00
João Moura
ecde504a79 updating gitignore 2024-05-02 21:57:49 -03:00
João Moura
0b781065d2 Better json parsing for smaller models 2024-05-02 21:57:41 -03:00
João Moura
bcb57ce5f9 updating git ignore 2024-05-02 20:52:43 -03:00
David Solito
6392a8cdd0 Update crew.py (#551)
Ad manager_agent description in crew docstring
2024-05-02 19:21:22 -03:00
João Moura
34e3dd24b4 new version 2024-05-02 05:00:29 -03:00
João Moura
c303d3730c cutting new version 2024-05-02 05:00:29 -03:00
João Moura
0a53ce17a2 small improvements for i18n 2024-05-02 05:00:29 -03:00
João Moura
7973651e05 new version 2024-05-02 05:00:29 -03:00
João Moura
672b150972 adding initial support for external prompt file 2024-05-02 05:00:29 -03:00
Jason Schrader
d8bcbd7d0a fix typos in generated readme (#345)
small things I noticed while upgrading our setup!
2024-05-02 03:32:18 -03:00
Dmitri Khokhlov
ff2f1477bb fix: TypeError: LongTermMemory.search() missing 1 required positional argument: 'latest_n' (#488)
Signed-off-by: Dmitri Khokhlov <dkhokhlov@gmail.com>
2024-05-02 03:28:36 -03:00
Ikko Eltociear Ashimine
1139073297 fix typo (#489)
* Update test_crew_function_calling_llm.yaml

ouput -> output

* Update tool_usage.py

ouput -> output
2024-05-02 03:27:40 -03:00
Sarvajith Adyanthaya
39deac2747 Changed "Inert" to "Innate" #470 (#490) 2024-05-02 03:27:09 -03:00
ftoppi
0a35868367 Update task.py: try to find json in task output using regex (#491)
* Update task.py: try to find json in task output using regex

Sometimes the model replies with a valid and additional text, let's try to extract and validate it first. It's cheaper than calling LLM for that.

* Update task.py

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-05-02 03:26:34 -03:00
Mosta
608f869789 Update PGSearchTool.md (#492)
typo on code snippet
2024-05-02 03:22:18 -03:00
Samuel Kocúr
c30bd1a18e fix db_storage_path handling to use env variable or cwd (#507) 2024-05-02 03:16:54 -03:00
Mish Ushakov
20a81af95f Added Browserbase loader to the docs (#508)
* Create BrowserbaseLoadTool.md

* added browserbase loader
2024-05-02 03:15:59 -03:00
deadlious
531c70b476 Tool name recognition based on string distance (#521)
* adding variations of ask question and delegate work tools

* Revert "adding variations of ask question and delegate work tools"

This reverts commit 38d4589be8.

* adding distance calculation for tool names.

* proper formatting

* remove brackets
2024-05-02 03:15:34 -03:00
Victor Carvalho Tavernari
dae0aedc99 Add conditional check for output file directory creation (#523)
This commit adds a conditional check to ensure that the output file directory exists before attempting to create it. This ensures that the code does not
fail in cases where the directory does not exist and needs to be created. The condition is added in the `_save_file` method of the `Task` class, ensuring
that the correct behavior is maintained for saving results to a file.
2024-05-02 03:13:51 -03:00
Jim Collins
5fde03f4b0 Update README.md (#525)
Reworded "If you want to also install crewai-tools, which is a package with tools that can be used by the agents, but more dependencies, you can install it with, example below uses it:" for clarity
2024-05-02 03:12:03 -03:00
Alex Fazio
48f53b529b fix to import statement PGSearchTool.md (#548)
fix to the import statement in PGSearchTool documentation
2024-05-02 03:10:43 -03:00
João Moura
4d9b0c6138 smal fixes and better guardrail for parsing small models tools usage 2024-05-02 02:21:59 -03:00
João Moura
70cabec876 Adding support for system, prompt and answe templates 2024-05-02 02:21:59 -03:00
João Moura
60423376cf removing unnecessary test 2024-05-02 02:21:59 -03:00
João Moura
22c646294a unifying co-worker string 2024-05-02 02:21:59 -03:00
João Moura
10b317cf34 remving blank line 2024-05-02 02:21:59 -03:00
João Moura
03f0c44cac Fixing task callback 2024-05-02 02:21:59 -03:00
João Moura
caa0e5db8d Revert "AgentOps Implementation (#411)"
This reverts commit 3d5257592b.
2024-05-02 02:21:59 -03:00
Alex Fazio
b862e464f8 docs fix to xml tool import statement (#546)
* docs fix to xml tool import statement

* Update XMLSearchTool.md
2024-05-01 12:53:49 -03:00
Braelyn Boynton
3d5257592b AgentOps Implementation (#411)
* implements agentops with a langchain handler, agent tracking and tool call recording

* track tool usage

* end session after completion

* track tool usage time

* better tool and llm tracking

* code cleanup

* make agentops optional

* optional dependency usage

* remove telemetry code

* optional agentops

* agentops version bump

* remove org key

* true dependency

* add crew org key to agentops

* cleanup

* Update pyproject.toml

* Revert "true dependency"

This reverts commit e52e8e9568.

* Revert "cleanup"

This reverts commit 7f5635fb9e.

* optional parent key

* agentops 0.1.5

* Revert "Revert "cleanup""

This reverts commit cea33d9a5d.

* Revert "Revert "true dependency""

This reverts commit 4d1b460b

* cleanup

* Forcing version 0.1.5

* Update pyproject.toml

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-20 12:20:13 -03:00
Elijas Dapšauskas
ff76715cd2 Allow minor version patches to python-dotenv (#339)
Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-19 02:44:08 -03:00
Emmanuel Crown
cdb0a9c953 Fixed a typo in the main readme on the llm selection , options for an agent (#349) 2024-04-19 02:42:04 -03:00
Sajal Sharma
b0acae81b0 Update LLM-Connections.md (#353)
Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-19 02:41:36 -03:00
Kaushal Powar
afc616d263 Update GitHubSearchTool.md (#357)
GithubSearchTool was misspelled as GitHubSearchTool

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-19 02:40:38 -03:00
Selim Erhan
e066b4dcb1 Update LLM-Connections.md (#359)
Created a short documentation on how to use Llama2 locally with crewAI thanks to the help of Ollama.

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-19 02:39:33 -03:00
Christian24
9ea495902e Fix lockfile (#477) 2024-04-18 11:28:06 -03:00
João Moura
d786c367b4 Update README.md 2024-04-17 00:02:49 -03:00
João Moura
a391004432 Adding manager llm 2024-04-16 16:50:44 -03:00
João Moura
dd97a2674d adding new installing crew docs 2024-04-16 16:50:44 -03:00
Joseph Bastulli
437c4c91bc fix: swapped the task callback assignment (#443) 2024-04-16 15:54:42 -03:00
Jack Hayter
575f1f98b0 Prevent duplicate TokenCalcHandler callbacks on Agent (#475) 2024-04-16 15:54:02 -03:00
Alex Reibman
2ee6ab6332 Incorrect documentation link for AgentOps (#458)
* remove .md

* made language more clear

* update images and documentation for spelling

* update typos and links

* update repo placement

* update wording

* clarify

* update wording

* Added clearer features

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-16 08:24:30 -03:00
Jonathan Morales Vélez
3d862538d2 fix link to observability (#461) 2024-04-16 08:22:11 -03:00
Preston Badeer
4bd36e0460 Update LLM-Connections.md with up to date LM Studio instructions (#468)
Co-authored-by: Preston Badeer <467756+pbadeer@users.noreply.github.com>
2024-04-16 08:20:56 -03:00
Eivind Hyldmo
7fbf0f1988 Fixed typo in Tools.md (#472) 2024-04-16 08:20:25 -03:00
Lennart J. Kurzweg
066127013b Added optional manager_agent parameter (#474)
* Added optional manager_agent parameter

* Update crew.py

---------

Co-authored-by: Lennart J. Kurzweg (Nx2) <git@nx2.site>
Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-04-16 08:18:36 -03:00
João Moura
f675208d72 cutting new version with updated cli template 2024-04-11 11:30:30 -03:00
João Moura
36aa69cf66 Preparing new version to use new version of crewai-tools 2024-04-10 11:52:12 -03:00
Cfomodz
66b77ffd08 Update README.md (#442) 2024-04-08 05:59:04 -03:00
João Moura
d2a3e4869a preparring new version 2024-04-08 02:08:57 -03:00
João Moura
a2dc7c7f31 adding missing import 2024-04-08 02:08:43 -03:00
João Moura
55ac69776a preparing new version 2024-04-08 01:39:22 -03:00
João Moura
7a7c9b0076 removing unnecessary certificate 2024-04-08 01:39:11 -03:00
João Moura
77d40230a8 preparing new version 2024-04-07 14:55:45 -03:00
João Moura
e4556040a8 fixing long temr memory interpolation 2024-04-07 14:55:35 -03:00
João Moura
755b3934a4 preping new verison with new tools package 2024-04-07 14:19:50 -03:00
João Moura
2d77fb72a5 preparing new version 2024-04-07 04:18:05 -03:00
96 changed files with 218120 additions and 11511 deletions

View File

@@ -1,10 +0,0 @@
name: Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable

16
.github/workflows/linter.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Requirements
run: |
pip install ruff
- name: Run Ruff Linter
run: ruff check --exclude "templates","__init__.py"

View File

@@ -14,19 +14,18 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: "3.11"
- name: Install Requirements
run: |
sudo apt-get update &&
pip install poetry &&
pip install poetry
poetry lock &&
poetry install
- name: Run tests
run: poetry run pytest
run: poetry run pytest tests

View File

@@ -1,4 +1,3 @@
name: Run Type Checks
on: [pull_request]
@@ -12,19 +11,16 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: "3.10"
- name: Install Requirements
run: |
sudo apt-get update &&
pip install poetry &&
poetry lock &&
poetry install
pip install mypy
- name: Run type checks
run: poetry run pyright
run: mypy src

5
.gitignore vendored
View File

@@ -8,4 +8,7 @@ assets/*
test/
docs_crew/
chroma.sqlite3
old_en.json
old_en.json
db/
test.py
rc-tests/*

View File

@@ -1,21 +1,9 @@
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.12.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
- id: black
language_version: python3.11
files: \.(py)$
exclude: 'src/crewai/cli/templates/(crew|main)\.py'
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (python)
args: ["--profile", "black", "--filter-files"]
- repo: https://github.com/PyCQA/autoflake
rev: v2.2.1
hooks:
- id: autoflake
args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variables', '--ignore-init-module-imports']
- id: ruff
args: ["--fix"]
exclude: "templates"
- id: ruff-format
exclude: "templates"

View File

@@ -30,7 +30,6 @@
- [Connecting Your Crew to a Model](#connecting-your-crew-to-a-model)
- [How CrewAI Compares](#how-crewai-compares)
- [Contribution](#contribution)
- [Hire CrewAI](#hire-crewai)
- [Telemetry](#telemetry)
- [License](#license)
@@ -49,7 +48,7 @@ To get started with CrewAI, follow these simple steps:
pip install crewai
```
If you want to also install crewai-tools, which is a package with tools that can be used by the agents, but more dependencies, you can install it with, example below uses it:
If you want to install the 'crewai' package along with its optional features that include additional tools for agents, you can do so by using the following command: pip install 'crewai[tools]'. This command installs the basic package and also adds extra components which require more dependencies to function."
```shell
pip install 'crewai[tools]'
@@ -83,7 +82,7 @@ researcher = Agent(
verbose=True,
allow_delegation=False,
tools=[search_tool]
# You can pass an optional llm attribute specifying what mode you wanna use.
# You can pass an optional llm attribute specifying what model you wanna use.
# It can be a local model through Ollama / LM Studio or a remote
# model like OpenAI, Mistral, Antrophic or others (https://docs.crewai.com/how-to/LLM-Connections/)
#
@@ -232,7 +231,7 @@ poetry run pytest
### Running static type checks
```bash
poetry run pyright
poetry run mypy
```
### Packaging
@@ -247,11 +246,6 @@ poetry build
pip install dist/*.tar.gz
```
## Hire CrewAI
We're a company developing crewAI and crewAI Enterprise, we for a limited time are offer consulting with selected customers, to get them early access to our enterprise solution
If you are interested on having access to it and hiring weekly hours with our team, feel free to email us at [joao@crewai.com](mailto:joao@crewai.com).
## Telemetry
CrewAI uses anonymous telemetry to collect usage data with the main purpose of helping us improve the library by focusing our efforts on the most used features, integrations and tools.
@@ -259,6 +253,7 @@ CrewAI uses anonymous telemetry to collect usage data with the main purpose of h
There is NO data being collected on the prompts, tasks descriptions agents backstories or goals nor tools usage, no API calls, nor responses nor any data that is being processed by the agents, nor any secrets and env vars.
Data collected includes:
- Version of crewAI
- So we can understand how many users are using the latest version
- Version of Python

View File

@@ -1463,11 +1463,11 @@
"locked": false,
"fontSize": 20,
"fontFamily": 3,
"text": "Agents have the inert ability of\nreach out to another to delegate\nwork or ask questions.",
"text": "Agents have the innate ability of\nreach out to another to delegate\nwork or ask questions.",
"textAlign": "right",
"verticalAlign": "top",
"containerId": null,
"originalText": "Agents have the inert ability of\nreach out to another to delegate\nwork or ask questions.",
"originalText": "Agents have the innate ability of\nreach out to another to delegate\nwork or ask questions.",
"lineHeight": 1.2,
"baseline": 68
},
@@ -1734,4 +1734,4 @@
"viewBackgroundColor": "#ffffff"
},
"files": {}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 263 KiB

View File

@@ -107,7 +107,7 @@ Here is a list of the available tools and their descriptions:
| **DirectoryReadTool** | Facilitates reading and processing of directory structures and their contents. |
| **FileReadTool** | Enables reading and extracting data from files, supporting various file formats. |
| **GithubSearchTool** | A RAG tool for searching within GitHub repositories, useful for code and documentation search.|
| **SeperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
| **SerperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
| **TXTSearchTool** | A RAG tool focused on searching within text (.txt) files, suitable for unstructured data. |
| **JSONSearchTool** | A RAG tool designed for searching within JSON files, catering to structured data handling. |
| **MDXSearchTool** | A RAG tool tailored for searching within Markdown (MDX) files, useful for documentation. |
@@ -221,4 +221,4 @@ agent = Agent(
```
## Conclusion
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively. When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling, caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively. When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling, caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.

View File

@@ -1,51 +1,52 @@
---
title: (AgentOps) Observability using AgentOps
title: Agent Monitoring with AgentOps
description: Understanding and logging your agent performance with AgentOps.
---
# Intro
Observability is a key aspect of developing and deploying conversational AI agents. It allows developers to understand how the agent is performing, how users are interacting with the agent, and how the agent is responding to user inputs.
Observability is a key aspect of developing and deploying conversational AI agents. It allows developers to understand how their agents are performing, how their agents are interacting with users, and how their agents use external tools and APIs. AgentOps is a product independent of CrewAI that provides a comprehensive observability solution for agents.
AgentOps is a product, idependent of crewAI that provides a comprehensive observability solution for agents.
This notebook will provide an overview of AgentOps and how to use it with crewAI.
## AgentOps
[AgentOps](https://agentops.ai) provides session replays, metrics, and monitoring for agents.
[AgentOps Repo](https://github.com/AgentOps-AI/agentops)
[AgentOps](https://agentops.ai/?=crew) provides session replays, metrics, and monitoring for agents.
At a high level, AgentOps gives you the ability to monitor cost, token usage, latency, agent failures, session-wide statistics, and more. For more info, check out the [AgentOps Repo](https://github.com/AgentOps-AI/agentops).
### Overview
AgentOps provides monotoring for agents in development and production. It provides a dashboard for monitoring agent performance, session replays, and custom reporting.
AgentOps provides monitoring for agents in development and production. It provides a dashboard for tracking agent performance, session replays, and custom reporting.
![agentops-overview.png](..%2Fassets%2Fagentops-overview.png)
Additionally, AgentOps provides session drilldowns for viewing Crew agent interactions, LLM calls, and tool usage in real-time. This feature is useful for debugging and understanding how agents interact with users as well as other agents.
Additionally, AgentOps provides session drilldowns that allows users to view the agent's interactions with users in real-time. This feature is useful for debugging and understanding how the agent interacts with users.
![agentops-session.png](..%2Fassets%2Fagentops-session.png)
![agentops-replay.png](..%2Fassets%2Fagentops-replay.png)
![Overview of a select series of agent session runs](..%2Fassets%2Fagentops-overview.png)
![Overview of session drilldowns for examining agent runs](..%2Fassets%2Fagentops-session.png)
![Viewing a step-by-step agent replay execution graph](..%2Fassets%2Fagentops-replay.png)
### Features
- LLM Cost management and tracking
- Replay Analytics
- Recursive thought detection
- Custom Reporting
- Analytics Dashboard
- Public Model Testing
- Custom Tests
- Time Travel Debugging
- Compliance and Security
- **LLM Cost Management and Tracking**: Track spend with foundation model providers
- **Replay Analytics**: Watch step-by-step agent execution graphs
- **Recursive Thought Detection**: Identify when agents fall into infinite loops
- **Custom Reporting**: Create custom analytics on agent performance
- **Analytics Dashboard**: Monitor high level statistics about agents in development and production
- **Public Model Testing**: Test your agents against benchmarks and leaderboards
- **Custom Tests**: Run your agents against domain specific tests
- **Time Travel Debugging**: Restart your sessions from checkpoints
- **Compliance and Security**: Create audit logs and detect potential threats such as profanity and PII leaks
- **Prompt Injection Detection**: Identify potential code injection and secret leaks
### Using AgentOps
Create a user API key here: app.agentops.ai/account
1. **Create an API Key:**
Create a user API key here: [Create API Key](app.agentops.ai/account)
2. **Configure Your Environment:**
Add your API key to your environment variables
```
AGENTOPS_API_KEY=<YOUR_AGENTOPS_API_KEY>
```
3. **Install AgentOps:**
Install AgentOps with:
```
pip install crewai[agentops]
@@ -62,11 +63,26 @@ import agentops
agentops.init()
```
This will initiate an AgentOps session as well as automatically track Crew agents. For further info on how to outfit more complex agentic systems, check out the [AgentOps documentation](https://docs.agentops.ai) or join the [Discord](https://discord.gg/j4f3KbeH).
### Crew + AgentOps Examples
- [Job Posting](https://github.com/joaomdmoura/crewAI-examples/tree/main/job-posting)
- [Markdown Validator](https://github.com/joaomdmoura/crewAI-examples/tree/main/markdown_validator)
- [Instagram Post](https://github.com/joaomdmoura/crewAI-examples/tree/main/instagram_post)
### Futher Information
To implement more features and better observability, please see the [AgentOps Repo](https://github.com/AgentOps-AI/agentops)
### Further Information
To get started, create an [AgentOps account](https://agentops.ai/?=crew).
For feature requests or bug reports, please reach out to the AgentOps team on the [AgentOps Repo](https://github.com/AgentOps-AI/agentops).
#### Extra links
<a href="https://twitter.com/agentopsai/">🐦 Twitter</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://discord.gg/JHPt4C7r">📢 Discord</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://app.agentops.ai/?=crew">🖇️ AgentOps Dashboard</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://docs.agentops.ai/introduction">📙 Documentation</a>

View File

@@ -0,0 +1,21 @@
---
title: Installing crewAI
description: A comprehensive guide to installing crewAI and its dependencies, including the latest updates and installation methods.
---
# Installing crewAI
Welcome to crewAI! This guide will walk you through the installation process for crewAI and its dependencies. crewAI is a flexible and powerful AI framework that enables you to create and manage AI agents, tools, and tasks efficiently. Let's get started!
## Installation
To install crewAI, you need to have Python >=3.10 and <=3.13 installed on your system:
```shell
# Install the mains crewAI package
pip install crewai
# Install the main crewAI package and the tools package
# that includes a series of helpful tools for your agents
pip install 'crewai[tools]'
```

View File

@@ -16,8 +16,8 @@ The `Agent` class is the cornerstone for implementing AI solutions in CrewAI. He
- `role`: Defines the agent's role within the solution.
- `goal`: Specifies the agent's objective.
- `backstory`: Provides a background story to the agent.
- `llm`: The language model that will run the agent. By default, it uses the GPT-4 model defined in the environment variable "OPENAI_MODEL_NAME".
- `function_calling_llm`: The language model that will handle the tool calling for this agent, overriding the crew function_calling_llm. Optional.
- `llm`: Indicates the Large Language Model the agent uses. By default, it uses the GPT-4 model defined in the environment variable "OPENAI_MODEL_NAME".
- `function_calling_llm` *Optional*: Will turn the ReAct crewAI agent into a function calling agent.
- `max_iter`: Maximum number of iterations for an agent to execute a task, default is 15.
- `memory`: Enables the agent to retain information during and a across executions. Default is `False`.
- `max_rpm`: Maximum number of requests per minute the agent's execution should respect. Optional.
@@ -42,7 +42,7 @@ example_agent = Agent(
```
## Ollama Integration
Ollama is preferred for local LLM integration, offering customization and privacy benefits. To integrate Ollama with CrewAI, set the appropriate environment variables as shown below. Note: Detailed Ollama setup is beyond this document's scope, but general guidance is provided.
Ollama is preferred for local LLM integration, offering customization and privacy benefits. To integrate Ollama with CrewAI, set the appropriate environment variables as shown below.
### Setting Up Ollama
- **Environment Variables Configuration**: To integrate Ollama, set the following environment variables:
@@ -52,6 +52,71 @@ OPENAI_MODEL_NAME='openhermes' # Adjust based on available model
OPENAI_API_KEY=''
```
## Ollama Integration (ex. for using Llama 2 locally)
1. [Download Ollama](https://ollama.com/download).
2. After setting up the Ollama, Pull the Llama2 by typing following lines into the terminal ```ollama pull llama2```.
3. Create a ModelFile similar the one below in your project directory.
```
FROM llama2
# Set parameters
PARAMETER temperature 0.8
PARAMETER stop Result
# Sets a custom system message to specify the behavior of the chat assistant
# Leaving it blank for now.
SYSTEM """"""
```
4. Create a script to get the base model, which in our case is llama2, and create a model on top of that with ModelFile above. PS: this will be ".sh" file.
```
#!/bin/zsh
# variables
model_name="llama2"
custom_model_name="crewai-llama2"
#get the base model
ollama pull $model_name
#create the model file
ollama create $custom_model_name -f ./Llama2ModelFile
```
5. Go into the directory where the script file and ModelFile is located and run the script.
6. Enjoy your free Llama2 model that powered up by excellent agents from crewai.
```
from crewai import Agent, Task, Crew
from langchain_openai import ChatOpenAI
import os
os.environ["OPENAI_API_KEY"] = "NA"
llm = ChatOpenAI(
model = "crewai-llama2",
base_url = "http://localhost:11434/v1")
general_agent = Agent(role = "Math Professor",
goal = """Provide the solution to the students that are asking mathematical questions and give them the answer.""",
backstory = """You are an excellent math professor that likes to solve math questions in a way that everyone can understand your solution""",
allow_delegation = False,
verbose = True,
llm = llm)
task = Task (description="""what is 3 + 5""",
agent = general_agent,
expected_output="A numerical answer.")
crew = Crew(
agents=[general_agent],
tasks=[task],
verbose=2
)
result = crew.kickoff()
print(result)
```
## HuggingFace Integration
There are a couple of different ways you can use HuggingFace to host your LLM.
@@ -97,10 +162,10 @@ OPENAI_API_KEY=NA
```
#### LM Studio
Launch [LM Studio](https://lmstudio.ai) and go to the Server tab. Then select a model from the dropdown menu then wait for it to load. Once it's loaded, click the green Start Server button and use the URL, port, and API key that's shown (you can modify them). Below is an example of the default settings as of LM Studio 0.2.19:
```sh
OPENAI_API_BASE="http://localhost:8000/v1"
OPENAI_MODEL_NAME=NA
OPENAI_API_KEY=NA
OPENAI_API_BASE="http://localhost:1234/v1"
OPENAI_API_KEY="lm-studio"
```
#### Mistral API

View File

@@ -43,6 +43,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
<div style="width:30%">
<h2>How-To Guides</h2>
<ul>
<li>
<a href="./how-to/Installing-CrewAI">
Installing crewAI
</a>
</li>
<li>
<a href="./how-to/Creating-a-Crew-and-kick-it-off">
Getting Started
@@ -79,8 +84,8 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
</a>
</li>
<li>
<a href="./how-to/AgentOps-Observability.md">
Agent Observability using AgentOps
<a href="./how-to/AgentOps-Observability">
Agent Monitoring with AgentOps
</a>
</li>
</ul>

View File

@@ -0,0 +1,29 @@
# BrowserbaseLoadTool
## Description
[Browserbase](https://browserbase.com) is a serverless platform for running headless browsers, it offers advanced debugging, session recordings, stealth mode, integrated proxies and captcha solving.
## Installation
- Get an API key from [browserbase.com](https://browserbase.com) and set it in environment variables (`BROWSERBASE_API_KEY`).
- Install the [Browserbase SDK](http://github.com/browserbase/python-sdk) along with `crewai[tools]` package:
```
pip install browserbase 'crewai[tools]'
```
## Example
Utilize the BrowserbaseLoadTool as follows to allow your agent to load websites:
```python
from crewai_tools import BrowserbaseLoadTool
tool = BrowserbaseLoadTool()
```
## Arguments
- `api_key`: Optional. Specifies Browserbase API key. Defaults is the `BROWSERBASE_API_KEY` environment variable.
- `text_content`: Optional. Load pages as readable text. Default is `False`.

View File

@@ -50,7 +50,7 @@ tool = CSVSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -41,7 +41,7 @@ Note: Substitute 'https://docs.example.com/reference' with your target documenta
By default, the tool uses OpenAI for both embeddings and summarization. To customize the model, you can use a config dictionary as follows:
```python
tool = YoutubeVideoSearchTool(
tool = CodeDocsSearchTool(
config=dict(
llm=dict(
provider="ollama", # or google, openai, anthropic, llama2, ...
@@ -53,7 +53,7 @@ tool = YoutubeVideoSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -48,7 +48,7 @@ tool = DOCXSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -43,7 +43,7 @@ tool = DirectorySearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -22,15 +22,15 @@ from crewai_tools import GithubSearchTool
# Initialize the tool for semantic searches within a specific GitHub repository
tool = GithubSearchTool(
github_repo='https://github.com/example/repo',
content_types=['code', 'issue'] # Options: code, repo, pr, issue
github_repo='https://github.com/example/repo',
content_types=['code', 'issue'] # Options: code, repo, pr, issue
)
# OR
# Initialize the tool for semantic searches within a specific GitHub repository, so the agent can search any repository if it learns about during its execution
tool = GithubSearchTool(
content_types=['code', 'issue'] # Options: code, repo, pr, issue
content_types=['code', 'issue'] # Options: code, repo, pr, issue
)
```
@@ -55,7 +55,7 @@ tool = GithubSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -48,7 +48,7 @@ tool = JSONSearchTool(
},
},
"embedder": {
"provider": "google",
"provider": "google", # or openai, ollama, ...
"config": {
"model": "models/embedding-001",
"task_type": "retrieval_document",

View File

@@ -49,7 +49,7 @@ tool = MDXSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -48,7 +48,7 @@ tool = PDFSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -19,7 +19,7 @@ pip install 'crewai[tools]'
Below is a proposed example showcasing how to use the PGSearchTool for conducting a semantic search on a table within a PostgreSQL database:
```python
rom crewai_tools import PGSearchTool
from crewai_tools import PGSearchTool
# Initialize the tool with the database URI and the target table name
tool = PGSearchTool(db_uri='postgresql://user:password@localhost:5432/mydatabase', table_name='employees')
@@ -48,7 +48,7 @@ tool = PGSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",
@@ -57,4 +57,4 @@ tool = PGSearchTool(
),
)
)
```
```

View File

@@ -50,7 +50,7 @@ tool = TXTSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -48,7 +48,7 @@ tool = WebsiteSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -17,7 +17,7 @@ pip install 'crewai[tools]'
Here are two examples demonstrating how to use the XMLSearchTool. The first example shows searching within a specific XML file, while the second example illustrates initiating a search without predefining an XML path, providing flexibility in search scope.
```python
from crewai_tools.tools.xml_search_tool import XMLSearchTool
from crewai_tools import XMLSearchTool
# Allow agents to search within any XML file's content as it learns about their paths during execution
tool = XMLSearchTool()
@@ -48,7 +48,7 @@ tool = XMLSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -48,7 +48,7 @@ tool = YoutubeChannelSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -52,7 +52,7 @@ tool = YoutubeVideoSearchTool(
),
),
embedder=dict(
provider="google",
provider="google", # or openai, ollama, ...
config=dict(
model="models/embedding-001",
task_type="retrieval_document",

View File

@@ -128,6 +128,7 @@ nav:
- Collaboration: 'core-concepts/Collaboration.md'
- Memory: 'core-concepts/Memory.md'
- How to Guides:
- Installing CrewAI: 'how-to/Installing-CrewAI.md'
- Getting Started: 'how-to/Creating-a-Crew-and-kick-it-off.md'
- Create Custom Tools: 'how-to/Create-Custom-Tools.md'
- Using Sequential Process: 'how-to/Sequential.md'
@@ -135,9 +136,10 @@ nav:
- Connecting to any LLM: 'how-to/LLM-Connections.md'
- Customizing Agents: 'how-to/Customizing-Agents.md'
- Human Input on Execution: 'how-to/Human-Input-on-Execution.md'
- Agent Observability using AgentOps: 'how-to/AgentOps-Observability.md'
- Agent Monitoring with AgentOps: 'how-to/AgentOps-Observability.md'
- Tools Docs:
- Google Serper Search: 'tools/SerperDevTool.md'
- Browserbase Web Loader: 'tools/BrowserbaseLoadTool.md'
- Scrape Website: 'tools/ScrapeWebsiteTool.md'
- Directory Read: 'tools/DirectoryReadTool.md'
- File Read: 'tools/FileReadTool.md'

1689
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
[tool.poetry]
name = "crewai"
version = "0.28.1"
version = "0.30.11"
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
authors = ["Joao Moura <joao@crewai.com>"]
readme = "README.md"
packages = [
{ include = "crewai", from = "src" },
]
packages = [{ include = "crewai", from = "src" }]
[tool.poetry.urls]
Homepage = "https://crewai.com"
@@ -23,9 +21,9 @@ opentelemetry-sdk = "^1.22.0"
opentelemetry-exporter-otlp-proto-http = "^1.22.0"
instructor = "^0.5.2"
regex = "^2023.12.25"
crewai-tools = { version = "^0.1.5", optional = true }
crewai-tools = { version = "^0.2.6", optional = true }
click = "^8.1.7"
python-dotenv = "1.0.0"
python-dotenv = "^1.0.0"
embedchain = "^0.1.98"
appdirs = "^1.4.4"
@@ -34,22 +32,17 @@ tools = ["crewai-tools"]
[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
pyright = ">=1.1.350,<2.0.0"
mypy = "1.10.0"
autoflake = "^2.2.1"
pre-commit = "^3.6.0"
mkdocs = "^1.4.3"
mkdocstrings = "^0.22.0"
mkdocstrings-python = "^1.1.2"
mkdocs-material = {extras = ["imaging"], version = "^9.5.7"}
mkdocs-material = { extras = ["imaging"], version = "^9.5.7" }
mkdocs-material-extensions = "^1.3.1"
pillow = "^10.2.0"
cairosvg = "^2.7.1"
crewai-tools = "^0.1.5"
[tool.isort]
profile = "black"
known_first_party = ["crewai"]
crewai-tools = "^0.2.6"
[tool.poetry.group.test.dependencies]
pytest = "^8.0.0"
@@ -59,6 +52,11 @@ python-dotenv = "1.0.0"
[tool.poetry.scripts]
crewai = "crewai.cli.cli:crewai"
[tool.mypy]
ignore_missing_imports = true
disable_error_code = 'import-untyped'
exclude = ["cli/templates/main.py", "cli/templates/crew.py"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@@ -121,6 +121,15 @@ class Agent(BaseModel):
callbacks: Optional[List[InstanceOf[BaseCallbackHandler]]] = Field(
default=None, description="Callback to be executed"
)
system_template: Optional[str] = Field(
default=None, description="System format for the agent."
)
prompt_template: Optional[str] = Field(
default=None, description="Prompt format for the agent."
)
response_template: Optional[str] = Field(
default=None, description="Response format for the agent."
)
_original_role: str | None = None
_original_goal: str | None = None
@@ -161,10 +170,16 @@ class Agent(BaseModel):
"""set agent executor is set."""
if hasattr(self.llm, "model_name"):
token_handler = TokenCalcHandler(self.llm.model_name, self._token_process)
if isinstance(self.llm.callbacks, list):
# Ensure self.llm.callbacks is a list
if not isinstance(self.llm.callbacks, list):
self.llm.callbacks = []
# Check if an instance of TokenCalcHandler already exists in the list
if not any(
isinstance(handler, TokenCalcHandler) for handler in self.llm.callbacks
):
self.llm.callbacks.append(token_handler)
else:
self.llm.callbacks = [token_handler]
if not self.agent_executor:
if not self.cache_handler:
@@ -189,7 +204,7 @@ class Agent(BaseModel):
Output of the agent
"""
if self.tools_handler:
self.tools_handler.last_used_tool = {}
self.tools_handler.last_used_tool = {} # type: ignore # Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "ToolCalling")
task_prompt = task.prompt()
@@ -209,7 +224,7 @@ class Agent(BaseModel):
task_prompt += self.i18n.slice("memory").format(memory=memory)
tools = tools or self.tools
parsed_tools = self._parse_tools(tools)
parsed_tools = self._parse_tools(tools) # type: ignore # Argument 1 to "_parse_tools" of "Agent" has incompatible type "list[Any] | None"; expected "list[Any]"
self.create_agent_executor(tools=tools)
self.agent_executor.tools = parsed_tools
@@ -288,11 +303,17 @@ class Agent(BaseModel):
}
if self._rpm_controller:
executor_args[
"request_within_rpm_limit"
] = self._rpm_controller.check_or_wait
executor_args["request_within_rpm_limit"] = (
self._rpm_controller.check_or_wait
)
prompt = Prompts(i18n=self.i18n, tools=tools).task_execution()
prompt = Prompts(
i18n=self.i18n,
tools=tools,
system_template=self.system_template,
prompt_template=self.prompt_template,
response_template=self.response_template,
).task_execution()
execution_prompt = prompt.partial(
goal=self.goal,
@@ -300,7 +321,13 @@ class Agent(BaseModel):
backstory=self.backstory,
)
bind = self.llm.bind(stop=[self.i18n.slice("observation")])
stop_words = [self.i18n.slice("observation")]
if self.response_template:
stop_words.append(
self.response_template.split("{{ .Response }}")[1].strip()
)
bind = self.llm.bind(stop=stop_words)
inner_agent = agent_args | execution_prompt | bind | CrewAgentParser(agent=self)
self.agent_executor = CrewAgentExecutor(
agent=RunnableAgent(runnable=inner_agent), **executor_args
@@ -337,7 +364,7 @@ class Agent(BaseModel):
thoughts += f"\n{observation_prefix}{observation}\n{llm_prefix}"
return thoughts
def _parse_tools(self, tools: List[Any]) -> List[LangChainTool]:
def _parse_tools(self, tools: List[Any]) -> List[LangChainTool]: # type: ignore # Function "langchain_core.tools.tool" is not valid as a type
"""Parse tools to be used for the task."""
# tentatively try to import from crewai_tools import BaseTool as CrewAITool
tools_list = []

View File

@@ -35,11 +35,14 @@ class CrewAgentExecutor(AgentExecutor):
crew: Any = None
function_calling_llm: Any = None
request_within_rpm_limit: Any = None
tools_handler: InstanceOf[ToolsHandler] = None
tools_handler: Optional[InstanceOf[ToolsHandler]] = None
max_iterations: Optional[int] = 15
have_forced_answer: bool = False
force_answer_max_iterations: Optional[int] = None
step_callback: Optional[Any] = None
system_template: Optional[str] = None
prompt_template: Optional[str] = None
response_template: Optional[str] = None
@root_validator()
def set_force_answer_max_iterations(cls, values: Dict) -> Dict:
@@ -81,9 +84,7 @@ class CrewAgentExecutor(AgentExecutor):
datetime=str(time.time()),
expected_output=self.task.expected_output,
metadata={
"suggestions": "\n".join(
[f"- {s}" for s in evaluation.suggestions]
),
"suggestions": evaluation.suggestions,
"quality": evaluation.quality,
},
)
@@ -115,6 +116,7 @@ class CrewAgentExecutor(AgentExecutor):
# Allowing human input given task setting
if self.task.human_input:
self.should_ask_for_human_input = True
# Let's start tracking the number of iterations and time elapsed
self.iterations = 0
time_elapsed = 0.0
@@ -130,8 +132,10 @@ class CrewAgentExecutor(AgentExecutor):
intermediate_steps,
run_manager=run_manager,
)
if self.step_callback:
self.step_callback(next_step_output)
if isinstance(next_step_output, AgentFinish):
# Creating long term memory
create_long_term_memory = threading.Thread(
@@ -185,7 +189,7 @@ class CrewAgentExecutor(AgentExecutor):
intermediate_steps = self._prepare_intermediate_steps(intermediate_steps)
# Call the LLM to see what to do.
output = self.agent.plan(
output = self.agent.plan( # type: ignore # Incompatible types in assignment (expression has type "AgentAction | AgentFinish | list[AgentAction]", variable has type "AgentAction")
intermediate_steps,
callbacks=run_manager.get_child() if run_manager else None,
**inputs,
@@ -271,8 +275,8 @@ class CrewAgentExecutor(AgentExecutor):
run_manager.on_agent_action(agent_action, color="green")
tool_usage = ToolUsage(
tools_handler=self.tools_handler,
tools=self.tools,
tools_handler=self.tools_handler, # type: ignore # Argument "tools_handler" to "ToolUsage" has incompatible type "ToolsHandler | None"; expected "ToolsHandler"
tools=self.tools, # type: ignore # Argument "tools" to "ToolUsage" has incompatible type "Sequence[BaseTool]"; expected "list[BaseTool]"
original_tools=self.original_tools,
tools_description=self.tools_description,
tools_names=self.tools_names,
@@ -294,7 +298,6 @@ class CrewAgentExecutor(AgentExecutor):
tool=tool_calling.tool_name,
tools=", ".join([tool.name.casefold() for tool in self.tools]),
)
yield AgentStep(action=agent_action, observation=observation)
def _ask_human_input(self, final_answer: dict) -> str:

View File

@@ -52,7 +52,6 @@ class CrewAgentParser(ReActSingleInputOutputParser):
action_input = action_match.group(2)
tool_input = action_input.strip(" ")
tool_input = tool_input.strip('"')
return AgentAction(action, tool_input, text)
elif includes_answer:

View File

@@ -8,13 +8,13 @@ from .cache.cache_handler import CacheHandler
class ToolsHandler:
"""Callback handler for tool usage."""
last_used_tool: ToolCalling = {}
cache: CacheHandler
last_used_tool: ToolCalling = {} # type: ignore # BUG?: Incompatible types in assignment (expression has type "Dict[...]", variable has type "ToolCalling")
cache: Optional[CacheHandler]
def __init__(self, cache: Optional[CacheHandler] = None):
"""Initialize the callback handler."""
self.cache = cache
self.last_used_tool = {}
self.last_used_tool = {} # type: ignore # BUG?: same as above
def on_tool_use(
self,
@@ -23,7 +23,7 @@ class ToolsHandler:
should_cache: bool = True,
) -> Any:
"""Run when tool ends running."""
self.last_used_tool = calling
self.last_used_tool = calling # type: ignore # BUG?: Incompatible types in assignment (expression has type "Union[ToolCalling, InstructorToolCalling]", variable has type "ToolCalling")
if self.cache and should_cache and calling.tool_name != CacheTools().name:
self.cache.add(
tool=calling.tool_name,

View File

@@ -1,6 +1,8 @@
import click
import pkg_resources
from .create_crew import create_crew
from .train_crew import train_crew
@click.group()
@@ -15,5 +17,36 @@ def create(project_name):
create_crew(project_name)
@crewai.command()
@click.option(
"--tools", is_flag=True, help="Show the installed version of crewai tools"
)
def version(tools):
"""Show the installed version of crewai."""
crewai_version = pkg_resources.get_distribution("crewai").version
click.echo(f"crewai version: {crewai_version}")
if tools:
try:
tools_version = pkg_resources.get_distribution("crewai-tools").version
click.echo(f"crewai tools version: {tools_version}")
except pkg_resources.DistributionNotFound:
click.echo("crewai tools not installed")
@crewai.command()
@click.option(
"-n",
"--n_iterations",
type=int,
default=5,
help="Number of iterations to train the crew",
)
def train(n_iterations: int):
"""Train the crew."""
click.echo(f"Training the crew for {n_iterations} iterations")
train_crew(n_iterations)
if __name__ == "__main__":
crewai()

View File

@@ -40,7 +40,7 @@ poetry run {{folder_name}}
This command initializes the {{name}} Crew, assembling the agents and assigning them tasks as defined in your configuration.
This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folser
This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder.
## Understanding Your Crew
@@ -51,7 +51,7 @@ The {{name}} Crew is composed of multiple AI agents, each with unique roles, goa
For support, questions, or feedback regarding the {{crew_name}} Crew or crewAI.
- Visit our [documentation](https://docs.crewai.com)
- Reach out to us through our [GitHub repository](https://github.com/joaomdmoura/crewai)
- [Joing our Discord](https://discord.com/invite/X4JWnZnxPb)
- [Chat wtih our docs](https://chatg.pt/DWjSBZn)
- [Join our Discord](https://discord.com/invite/X4JWnZnxPb)
- [Chat with our docs](https://chatg.pt/DWjSBZn)
Let's create wonders together with the power and simplicity of crewAI.
Let's create wonders together with the power and simplicity of crewAI.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
import sys
from {{folder_name}}.crew import {{crew_name}}Crew
@@ -7,4 +8,15 @@ def run():
inputs = {
'topic': 'AI LLMs'
}
{{crew_name}}Crew().crew().kickoff(inputs=inputs)
{{crew_name}}Crew().crew().kickoff(inputs=inputs)
def train():
"""
Train the crew for a given number of iterations.
"""
try:
{{crew_name}}Crew().crew().train(n_iterations=int(sys.argv[1]))
except Exception as e:
raise Exception(f"An error occurred while training the crew: {e}")

View File

@@ -6,11 +6,12 @@ authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
crewai = {extras = ["tools"], version = "^0.27.0"}
crewai = { extras = ["tools"], version = "^0.30.11" }
[tool.poetry.scripts]
{{folder_name}} = "{{folder_name}}.main:run"
train = "{{folder_name}}.main:train"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
build-backend = "poetry.core.masonry.api"

View File

@@ -3,7 +3,9 @@ from crewai_tools import BaseTool
class MyCustomTool(BaseTool):
name: str = "Name of my tool"
description: str = "Clear description for what this tool is useful for, you agent will need this information to use it."
description: str = (
"Clear description for what this tool is useful for, you agent will need this information to use it."
)
def _run(self, argument: str) -> str:
# Implementation goes here

View File

@@ -0,0 +1,29 @@
import subprocess
import click
def train_crew(n_iterations: int) -> None:
"""
Train the crew by running a command in the Poetry environment.
Args:
n_iterations (int): The number of iterations to train the crew.
"""
command = ["poetry", "run", "train", str(n_iterations)]
try:
if n_iterations <= 0:
raise ValueError("The number of iterations must be a positive integer.")
result = subprocess.run(command, capture_output=False, text=True, check=True)
if result.stderr:
click.echo(result.stderr, err=True)
except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while training the crew: {e}", err=True)
click.echo(e.output, err=True)
except Exception as e:
click.echo(f"An unexpected error occurred: {e}", err=True)

View File

@@ -25,7 +25,7 @@ from crewai.process import Process
from crewai.task import Task
from crewai.telemetry import Telemetry
from crewai.tools.agent_tools import AgentTools
from crewai.utilities import I18N, Logger, RPMController, FileHandler
from crewai.utilities import I18N, FileHandler, Logger, RPMController
class Crew(BaseModel):
@@ -36,6 +36,7 @@ class Crew(BaseModel):
tasks: List of tasks assigned to the crew.
agents: List of agents part of this crew.
manager_llm: The language model that will run manager agent.
manager_agent: Custom agent that will be used as manager.
memory: Whether the crew should use memory to store memories of it's execution.
manager_callbacks: The callback handlers to be executed by the manager agent when hierarchical process is used
cache: Whether the crew should use a cache to store the results of the tools execution.
@@ -44,6 +45,7 @@ class Crew(BaseModel):
verbose: Indicates the verbosity level for logging during execution.
config: Configuration settings for the crew.
max_rpm: Maximum number of requests per minute for the crew execution to be respected.
prompt_file: Path to the prompt json file to be used for the crew.
id: A unique identifier for the crew instance.
full_output: Whether the crew should return the full output with all tasks outputs or just the final output.
task_callback: Callback to be executed after each task for every agents execution.
@@ -86,6 +88,9 @@ class Crew(BaseModel):
manager_llm: Optional[Any] = Field(
description="Language model that will run the agent.", default=None
)
manager_agent: Optional[Any] = Field(
description="Custom agent that will be used as manager.", default=None
)
manager_callbacks: Optional[List[InstanceOf[BaseCallbackHandler]]] = Field(
default=None,
description="A list of callback handlers to be executed by the manager agent when hierarchical process is used",
@@ -108,13 +113,9 @@ class Crew(BaseModel):
default=None,
description="Maximum number of requests per minute for the crew execution to be respected.",
)
language: str = Field(
default="en",
description="Language used for the crew, defaults to English.",
)
language_file: str = Field(
prompt_file: str = Field(
default=None,
description="Path to the language file to be used for the crew.",
description="Path to the prompt json file to be used for the crew.",
)
output_log_file: Optional[Union[bool, str]] = Field(
default=False,
@@ -163,19 +164,32 @@ class Crew(BaseModel):
"""Set private attributes."""
if self.memory:
self._long_term_memory = LongTermMemory()
self._short_term_memory = ShortTermMemory(embedder_config=self.embedder)
self._entity_memory = EntityMemory(embedder_config=self.embedder)
self._short_term_memory = ShortTermMemory(
crew=self, embedder_config=self.embedder
)
self._entity_memory = EntityMemory(crew=self, embedder_config=self.embedder)
return self
@model_validator(mode="after")
def check_manager_llm(self):
"""Validates that the language model is set when using hierarchical process."""
if self.process == Process.hierarchical and not self.manager_llm:
raise PydanticCustomError(
"missing_manager_llm",
"Attribute `manager_llm` is required when using hierarchical process.",
{},
)
if self.process == Process.hierarchical:
if not self.manager_llm and not self.manager_agent:
raise PydanticCustomError(
"missing_manager_llm_or_manager_agent",
"Attribute `manager_llm` or `manager_agent` is required when using hierarchical process.",
{},
)
if (self.manager_agent is not None) and (
self.agents.count(self.manager_agent) > 0
):
raise PydanticCustomError(
"manager_agent_in_agents",
"Manager agent should not be included in agents list.",
{},
)
return self
@model_validator(mode="after")
@@ -230,10 +244,10 @@ class Crew(BaseModel):
def kickoff(self, inputs: Optional[Dict[str, Any]] = {}) -> str:
"""Starts the crew to work on its assigned tasks."""
self._execution_span = self._telemetry.crew_execution_span(self)
self._interpolate_inputs(inputs)
self._interpolate_inputs(inputs) # type: ignore # Argument 1 to "_interpolate_inputs" of "Crew" has incompatible type "dict[str, Any] | None"; expected "dict[str, Any]"
self._set_tasks_callbacks()
i18n = I18N(language=self.language, language_file=self.language_file)
i18n = I18N(prompt_file=self.prompt_file)
for agent in self.agents:
agent.i18n = i18n
@@ -251,8 +265,8 @@ class Crew(BaseModel):
if self.process == Process.sequential:
result = self._run_sequential_process()
elif self.process == Process.hierarchical:
result, manager_metrics = self._run_hierarchical_process()
metrics.append(manager_metrics)
result, manager_metrics = self._run_hierarchical_process() # type: ignore # Unpacking a string is disallowed
metrics.append(manager_metrics) # type: ignore # Cannot determine type of "manager_metrics"
else:
raise NotImplementedError(
@@ -268,11 +282,15 @@ class Crew(BaseModel):
return result
def train(self, n_iterations: int) -> None:
# TODO: Implement training
pass
def _run_sequential_process(self) -> str:
"""Executes tasks sequentially and returns the final output."""
task_output = ""
for task in self.tasks:
if task.agent.allow_delegation:
if task.agent.allow_delegation: # type: ignore # Item "None" of "Agent | None" has no attribute "allow_delegation"
agents_for_delegation = [
agent for agent in self.agents if agent != task.agent
]
@@ -306,15 +324,22 @@ class Crew(BaseModel):
def _run_hierarchical_process(self) -> str:
"""Creates and assigns a manager agent to make sure the crew completes the tasks."""
i18n = I18N(language=self.language, language_file=self.language_file)
manager = Agent(
role=i18n.retrieve("hierarchical_manager_agent", "role"),
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
tools=AgentTools(agents=self.agents).tools(),
llm=self.manager_llm,
verbose=True,
)
i18n = I18N(prompt_file=self.prompt_file)
if self.manager_agent is not None:
self.manager_agent.allow_delegation = True
manager = self.manager_agent
if len(manager.tools) > 0:
raise Exception("Manager agent should not have tools")
manager.tools = AgentTools(agents=self.agents).tools()
else:
manager = Agent(
role=i18n.retrieve("hierarchical_manager_agent", "role"),
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
tools=AgentTools(agents=self.agents).tools(),
llm=self.manager_llm,
verbose=True,
)
task_output = ""
for task in self.tasks:
@@ -338,22 +363,23 @@ class Crew(BaseModel):
)
self._finish_execution(task_output)
return self._format_output(task_output), manager._token_process.get_summary()
return self._format_output(task_output), manager._token_process.get_summary() # type: ignore # Incompatible return value type (got "tuple[str, Any]", expected "str")
def _set_tasks_callbacks(self) -> str:
def _set_tasks_callbacks(self) -> None:
"""Sets callback for every task suing task_callback"""
for task in self.tasks:
task.callback = self.task_callback
if not task.callback:
task.callback = self.task_callback
def _interpolate_inputs(self, inputs: Dict[str, Any]) -> str:
def _interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
"""Interpolates the inputs in the tasks and agents."""
[task.interpolate_inputs(inputs) for task in self.tasks]
[agent.interpolate_inputs(inputs) for agent in self.agents]
[task.interpolate_inputs(inputs) for task in self.tasks] # type: ignore # "interpolate_inputs" of "Task" does not return a value (it only ever returns None)
[agent.interpolate_inputs(inputs) for agent in self.agents] # type: ignore # "interpolate_inputs" of "Agent" does not return a value (it only ever returns None)
def _format_output(self, output: str) -> str:
"""Formats the output of the crew execution."""
if self.full_output:
return {
return { # type: ignore # Incompatible return value type (got "dict[str, Sequence[str | TaskOutput | None]]", expected "str")
"final_output": output,
"tasks_outputs": [task.output for task in self.tasks if task],
}

View File

@@ -1,3 +1,5 @@
from typing import Optional
from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory
@@ -32,18 +34,23 @@ class ContextualMemory:
formatted_results = "\n".join([f"- {result}" for result in stm_results])
return f"Recent Insights:\n{formatted_results}" if stm_results else ""
def _fetch_ltm_context(self, task) -> str:
def _fetch_ltm_context(self, task) -> Optional[str]:
"""
Fetches historical data or insights from LTM that are relevant to the task's description and expected_output,
formatted as bullet points.
"""
ltm_results = self.ltm.search(task)
ltm_results = self.ltm.search(task, latest_n=2)
if not ltm_results:
return None
formatted_results = "\n".join(
[f"{result['metadata']['suggestions']}" for result in ltm_results]
)
formatted_results = list(set(formatted_results.split('\n')))
formatted_results = [
suggestion
for result in ltm_results
for suggestion in result["metadata"]["suggestions"] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice"
]
formatted_results = list(dict.fromkeys(formatted_results))
formatted_results = "\n".join([f"- {result}" for result in formatted_results]) # type: ignore # Incompatible types in assignment (expression has type "str", variable has type "list[str]")
return f"Historical Data:\n{formatted_results}" if ltm_results else ""
def _fetch_entity_context(self, query) -> str:
@@ -53,6 +60,6 @@ class ContextualMemory:
"""
em_results = self.em.search(query)
formatted_results = "\n".join(
[f"- {result['context']}" for result in em_results]
[f"- {result['context']}" for result in em_results] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice"
)
return f"Entities:\n{formatted_results}" if em_results else ""

View File

@@ -10,13 +10,16 @@ class EntityMemory(Memory):
Inherits from the Memory class.
"""
def __init__(self, embedder_config=None):
def __init__(self, crew=None, embedder_config=None):
storage = RAGStorage(
type="entities", allow_reset=False, embedder_config=embedder_config
type="entities",
allow_reset=False,
embedder_config=embedder_config,
crew=crew,
)
super().__init__(storage)
def save(self, item: EntityMemoryItem) -> None:
def save(self, item: EntityMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"
"""Saves an entity item into the SQLite storage."""
data = f"{item.name}({item.type}): {item.description}"
super().save(data, item.metadata)

View File

@@ -18,15 +18,15 @@ class LongTermMemory(Memory):
storage = LTMSQLiteStorage()
super().__init__(storage)
def save(self, item: LongTermMemoryItem) -> None:
def save(self, item: LongTermMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"
metadata = item.metadata
metadata.update({"agent": item.agent, "expected_output": item.expected_output})
self.storage.save(
self.storage.save( # type: ignore # BUG?: Unexpected keyword argument "task_description","score","datetime" for "save" of "Storage"
task_description=item.task,
score=metadata["quality"],
metadata=metadata,
datetime=item.datetime,
)
def search(self, task: str) -> Dict[str, Any]:
return self.storage.load(task)
def search(self, task: str, latest_n: int = 3) -> Dict[str, Any]:
return self.storage.load(task, latest_n) # type: ignore # BUG?: "Storage" has no attribute "load"

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, Union
from typing import Any, Dict, Optional, Union
class LongTermMemoryItem:
@@ -8,8 +8,8 @@ class LongTermMemoryItem:
task: str,
expected_output: str,
datetime: str,
quality: Union[int, float] = None,
metadata: Dict[str, Any] = None,
quality: Optional[Union[int, float]] = None,
metadata: Optional[Dict[str, Any]] = None,
):
self.task = task
self.agent = agent

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict
from typing import Any, Dict, Optional
from crewai.memory.storage.interface import Storage
@@ -12,12 +12,16 @@ class Memory:
self.storage = storage
def save(
self, value: Any, metadata: Dict[str, Any] = None, agent: str = None
self,
value: Any,
metadata: Optional[Dict[str, Any]] = None,
agent: Optional[str] = None,
) -> None:
metadata = metadata or {}
if agent:
metadata["agent"] = agent
self.storage.save(value, metadata)
self.storage.save(value, metadata) # type: ignore # Maybe BUG? Should be self.storage.save(key, value, metadata)
def search(self, query: str) -> Dict[str, Any]:
return self.storage.search(query)

View File

@@ -12,12 +12,14 @@ class ShortTermMemory(Memory):
MemoryItem instances.
"""
def __init__(self, embedder_config=None):
storage = RAGStorage(type="short_term", embedder_config=embedder_config)
def __init__(self, crew=None, embedder_config=None):
storage = RAGStorage(
type="short_term", embedder_config=embedder_config, crew=crew
)
super().__init__(storage)
def save(self, item: ShortTermMemoryItem) -> None:
def save(self, item: ShortTermMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"
super().save(item.data, item.metadata, item.agent)
def search(self, query: str, score_threshold: float = 0.35):
return self.storage.search(query=query, score_threshold=score_threshold)
return self.storage.search(query=query, score_threshold=score_threshold) # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters

View File

@@ -1,8 +1,10 @@
from typing import Any, Dict
from typing import Any, Dict, Optional
class ShortTermMemoryItem:
def __init__(self, data: Any, agent: str, metadata: Dict[str, Any] = None):
def __init__(
self, data: Any, agent: str, metadata: Optional[Dict[str, Any]] = None
):
self.data = data
self.agent = agent
self.metadata = metadata if metadata is not None else {}

View File

@@ -7,5 +7,5 @@ class Storage:
def save(self, key: str, value: Any, metadata: Dict[str, Any]) -> None:
pass
def search(self, key: str) -> Dict[str, Any]:
def search(self, key: str) -> Dict[str, Any]: # type: ignore
pass

View File

@@ -1,6 +1,6 @@
import json
import sqlite3
from typing import Any, Dict, Union
from typing import Any, Dict, List, Optional, Union
from crewai.utilities import Printer
from crewai.utilities.paths import db_storage_path
@@ -11,7 +11,9 @@ class LTMSQLiteStorage:
An updated SQLite storage class for LTM data storage.
"""
def __init__(self, db_path=f"{db_storage_path()}/long_term_memory_storage.db"):
def __init__(
self, db_path: str = f"{db_storage_path()}/long_term_memory_storage.db"
) -> None:
self.db_path = db_path
self._printer: Printer = Printer()
self._initialize_db()
@@ -67,19 +69,21 @@ class LTMSQLiteStorage:
color="red",
)
def load(self, task_description: str) -> Dict[str, Any]:
def load(
self, task_description: str, latest_n: int
) -> Optional[List[Dict[str, Any]]]:
"""Queries the LTM table by task description with error handling."""
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute(
"""
SELECT metadata, datetime, score
FROM long_term_memories
WHERE task_description = ?
ORDER BY datetime DESC, score ASC
LIMIT 2
""",
f"""
SELECT metadata, datetime, score
FROM long_term_memories
WHERE task_description = ?
ORDER BY datetime DESC, score ASC
LIMIT {latest_n}
""",
(task_description,),
)
rows = cursor.fetchall()

View File

@@ -2,7 +2,7 @@ import contextlib
import io
import logging
import os
from typing import Any, Dict
from typing import Any, Dict, List, Optional
from embedchain import App
from embedchain.llm.base import BaseLlm
@@ -37,13 +37,18 @@ class RAGStorage(Storage):
search efficiency.
"""
def __init__(self, type, allow_reset=True, embedder_config=None):
def __init__(self, type, allow_reset=True, embedder_config=None, crew=None):
super().__init__()
if (
not os.getenv("OPENAI_API_KEY")
and not os.getenv("OPENAI_BASE_URL") == "https://api.openai.com/v1"
):
os.environ["OPENAI_API_KEY"] = "fake"
agents = crew.agents if crew else []
agents = [agent.role for agent in agents]
agents = "_".join(agents)
config = {
"app": {
"config": {"name": type, "collect_metrics": False, "log_level": "ERROR"}
@@ -58,7 +63,7 @@ class RAGStorage(Storage):
"provider": "chroma",
"config": {
"collection_name": type,
"dir": f"{db_storage_path()}/{type}",
"dir": f"{db_storage_path()}/{type}/{agents}",
"allow_reset": allow_reset,
},
},
@@ -72,16 +77,16 @@ class RAGStorage(Storage):
if allow_reset:
self.app.reset()
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
def save(self, value: Any, metadata: Dict[str, Any]) -> None: # type: ignore # BUG?: Should be save(key, value, metadata) Signature of "save" incompatible with supertype "Storage"
self._generate_embedding(value, metadata)
def search(
def search( # type: ignore # BUG?: Signature of "search" incompatible with supertype "Storage"
self,
query: str,
limit: int = 3,
filter: dict = None,
filter: Optional[dict] = None,
score_threshold: float = 0.35,
) -> Dict[str, Any]:
) -> List[Any]:
with suppress_logging():
try:
results = (

View File

@@ -1,14 +1,28 @@
tasks_order = []
def memoize(func):
cache = {}
def memoized_func(*args, **kwargs):
key = (args, tuple(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoized_func
def task(func):
func.is_task = True
tasks_order.append(func.__name__)
func = memoize(func)
return func
def agent(func):
func.is_agent = True
func = memoize(func)
return func

View File

@@ -4,13 +4,15 @@ from pathlib import Path
import yaml
from dotenv import load_dotenv
from pydantic import ConfigDict
load_dotenv()
def CrewBase(cls):
class WrappedClass(cls):
is_crew_class = True
model_config = ConfigDict(arbitrary_types_allowed=True)
is_crew_class: bool = True # type: ignore
base_directory = None
for frame_info in inspect.stack():
@@ -40,6 +42,7 @@ def CrewBase(cls):
@staticmethod
def load_yaml(config_path: str):
with open(config_path, "r") as file:
# parsedContent = YamlParser.parse(file) # type: ignore # Argument 1 to "parse" has incompatible type "TextIOWrapper"; expected "YamlParser"
return yaml.safe_load(file)
return WrappedClass

View File

@@ -1,3 +1,5 @@
import os
import re
import threading
import uuid
from typing import Any, Dict, List, Optional, Type
@@ -39,7 +41,7 @@ class Task(BaseModel):
tools_errors: int = 0
delegations: int = 0
i18n: I18N = I18N()
thread: threading.Thread = None
thread: Optional[threading.Thread] = None
prompt_context: Optional[str] = None
description: str = Field(description="Description of the actual task.")
expected_output: str = Field(
@@ -107,6 +109,14 @@ class Task(BaseModel):
"may_not_set_field", "This field is not to be set by the user.", {}
)
@field_validator("output_file")
@classmethod
def output_file_validattion(cls, value: str) -> str:
"""Validate the output file path by removing the / from the beginning of the path."""
if value.startswith("/"):
return value[1:]
return value
@model_validator(mode="after")
def set_attributes_based_on_config(self) -> "Task":
"""Set attributes based on the agent configuration."""
@@ -134,7 +144,7 @@ class Task(BaseModel):
)
return self
def execute(
def execute( # type: ignore # Missing return statement
self,
agent: Agent | None = None,
context: Optional[str] = None,
@@ -153,13 +163,13 @@ class Task(BaseModel):
)
if self.context:
context = []
context = [] # type: ignore # Incompatible types in assignment (expression has type "list[Never]", variable has type "str | None")
for task in self.context:
if task.async_execution:
task.thread.join()
task.thread.join() # type: ignore # Item "None" of "Thread | None" has no attribute "join"
if task and task.output:
context.append(task.output.raw_output)
context = "\n".join(context)
context.append(task.output.raw_output) # type: ignore # Item "str" of "str | None" has no attribute "append"
context = "\n".join(context) # type: ignore # Argument 1 to "join" of "str" has incompatible type "str | None"; expected "Iterable[str]"
self.prompt_context = context
tools = tools or self.tools
@@ -240,17 +250,26 @@ class Task(BaseModel):
# try to convert task_output directly to pydantic/json
try:
exported_result = model.model_validate_json(result)
exported_result = model.model_validate_json(result) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
if self.output_json:
return exported_result.model_dump()
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result
except Exception:
pass
# sometimes the response contains valid JSON in the middle of text
match = re.search(r"({.*})", result, re.DOTALL)
if match:
try:
exported_result = model.model_validate_json(match.group(0)) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
if self.output_json:
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result
except Exception:
pass
llm = self.agent.function_calling_llm or self.agent.llm
llm = self.agent.function_calling_llm or self.agent.llm # type: ignore # Item "None" of "Agent | None" has no attribute "function_calling_llm"
if not self._is_gpt(llm):
model_schema = PydanticSchemaParser(model=model).get_schema()
model_schema = PydanticSchemaParser(model=model).get_schema() # type: ignore # Argument "model" to "PydanticSchemaParser" has incompatible type "type[BaseModel] | None"; expected "type[BaseModel]"
instructions = f"{instructions}\n\nThe json should have the following structure, with the following keys:\n{model_schema}"
converter = Converter(
@@ -271,17 +290,22 @@ class Task(BaseModel):
if self.output_file:
content = (
exported_result if not self.output_pydantic else exported_result.json()
exported_result if not self.output_pydantic else exported_result.json() # type: ignore # "str" has no attribute "json"
)
self._save_file(content)
return exported_result
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base == None
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None
def _save_file(self, result: Any) -> None:
with open(self.output_file, "w") as file:
directory = os.path.dirname(self.output_file) # type: ignore # Value of type variable "AnyOrLiteralStr" of "dirname" cannot be "str | None"
if directory and not os.path.exists(directory):
os.makedirs(directory)
with open(self.output_file, "w", encoding="utf-8") as file: # type: ignore # Argument 1 to "open" has incompatible type "str | None"; expected "int | str | bytes | PathLike[str] | PathLike[bytes]"
file.write(result)
return None

View File

@@ -1,46 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz
ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD
EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw
MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI
ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT
D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk
IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z
wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD
VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD
VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz
dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/
BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD
Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1
c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs
cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9
4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4
MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5
MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL
q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc
JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA
FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1
xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI
MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j
b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG
CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM
BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy
ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+
FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV
bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY
CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf
8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=
-----END CERTIFICATE-----

View File

@@ -1,5 +1,4 @@
import asyncio
import importlib.resources
import json
import os
import platform
@@ -41,19 +40,17 @@ class Telemetry:
def __init__(self):
self.ready = False
self.trace_set = False
try:
telemetry_endpoint = "https://telemetry.crewai.com:4319"
self.resource = Resource(
attributes={SERVICE_NAME: "crewAI-telemetry"},
)
self.provider = TracerProvider(resource=self.resource)
cert_file = importlib.resources.files("crewai.telemetry").joinpath(
"STAR_crewai_com_bundle.pem"
)
processor = BatchSpanProcessor(
OTLPSpanExporter(
endpoint=f"{telemetry_endpoint}/v1/traces",
certificate_file=cert_file,
timeout=30,
)
)
@@ -69,13 +66,13 @@ class Telemetry:
self.ready = False
def set_tracer(self):
if self.ready:
provider = trace.get_tracer_provider()
if provider is None:
try:
trace.set_tracer_provider(self.provider)
except Exception:
self.ready = False
if self.ready and not self.trace_set:
try:
trace.set_tracer_provider(self.provider)
self.trace_set = True
except Exception:
self.ready = False
self.trace_set = False
def crew_creation(self, crew):
"""Records the creation of a crew."""
@@ -91,7 +88,10 @@ class Telemetry:
self._add_attribute(span, "python_version", platform.python_version())
self._add_attribute(span, "crew_id", str(crew.id))
self._add_attribute(span, "crew_process", crew.process)
self._add_attribute(span, "crew_language", crew.language)
self._add_attribute(
span, "crew_language", crew.prompt_file if crew.i18n else "None"
)
self._add_attribute(span, "crew_memory", crew.memory)
self._add_attribute(span, "crew_number_of_tasks", len(crew.tasks))
self._add_attribute(span, "crew_number_of_agents", len(crew.agents))
self._add_attribute(
@@ -102,11 +102,10 @@ class Telemetry:
{
"id": str(agent.id),
"role": agent.role,
"memory_enabled?": agent.memory,
"verbose?": agent.verbose,
"max_iter": agent.max_iter,
"max_rpm": agent.max_rpm,
"i18n": agent.i18n.language,
"i18n": agent.i18n.prompt_file,
"llm": json.dumps(self._safe_llm_attributes(agent.llm)),
"delegation_enabled?": agent.allow_delegation,
"tools_names": [
@@ -150,11 +149,17 @@ class Telemetry:
try:
tracer = trace.get_tracer("crewai.telemetry")
span = tracer.start_span("Tool Repeated Usage")
self._add_attribute(
span,
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(span, "tool_name", tool_name)
self._add_attribute(span, "attempts", attempts)
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
)
if llm:
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
)
span.set_status(Status(StatusCode.OK))
span.end()
except Exception:
@@ -166,11 +171,17 @@ class Telemetry:
try:
tracer = trace.get_tracer("crewai.telemetry")
span = tracer.start_span("Tool Usage")
self._add_attribute(
span,
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(span, "tool_name", tool_name)
self._add_attribute(span, "attempts", attempts)
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
)
if llm:
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
)
span.set_status(Status(StatusCode.OK))
span.end()
except Exception:
@@ -183,8 +194,14 @@ class Telemetry:
tracer = trace.get_tracer("crewai.telemetry")
span = tracer.start_span("Tool Usage Error")
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
span,
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
if llm:
self._add_attribute(
span, "llm", json.dumps(self._safe_llm_attributes(llm))
)
span.set_status(Status(StatusCode.OK))
span.end()
except Exception:
@@ -198,6 +215,11 @@ class Telemetry:
try:
tracer = trace.get_tracer("crewai.telemetry")
span = tracer.start_span("Crew Execution")
self._add_attribute(
span,
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(span, "crew_id", str(crew.id))
self._add_attribute(
span,
@@ -209,11 +231,10 @@ class Telemetry:
"role": agent.role,
"goal": agent.goal,
"backstory": agent.backstory,
"memory_enabled?": agent.memory,
"verbose?": agent.verbose,
"max_iter": agent.max_iter,
"max_rpm": agent.max_rpm,
"i18n": agent.i18n.language,
"i18n": agent.i18n.prompt_file,
"llm": json.dumps(self._safe_llm_attributes(agent.llm)),
"delegation_enabled?": agent.allow_delegation,
"tools_names": [
@@ -235,9 +256,11 @@ class Telemetry:
"async_execution?": task.async_execution,
"output": task.expected_output,
"agent_role": task.agent.role if task.agent else "None",
"context": [task.description for task in task.context]
if task.context
else "None",
"context": (
[task.description for task in task.context]
if task.context
else "None"
),
"tools_names": [
tool.name.casefold() for tool in task.tools
],
@@ -253,6 +276,11 @@ class Telemetry:
def end_crew(self, crew, output):
if (self.ready) and (crew.share_crew):
try:
self._add_attribute(
crew._execution_span,
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(crew._execution_span, "crew_output", output)
self._add_attribute(
crew._execution_span,
@@ -282,6 +310,8 @@ class Telemetry:
def _safe_llm_attributes(self, llm):
attributes = ["name", "model_name", "base_url", "model", "top_k", "temperature"]
safe_attributes = {k: v for k, v in vars(llm).items() if k in attributes}
safe_attributes["class"] = llm.__class__.__name__
return safe_attributes
if llm:
safe_attributes = {k: v for k, v in vars(llm).items() if k in attributes}
safe_attributes["class"] = llm.__class__.__name__
return safe_attributes
return {}

View File

@@ -1,4 +1,4 @@
from typing import List
from typing import List, Union
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
@@ -33,12 +33,26 @@ class AgentTools(BaseModel):
]
return tools
def delegate_work(self, coworker: str, task: str, context: str):
"""Useful to delegate a specific task to a coworker passing all necessary context and names."""
def delegate_work(
self, task: str, context: str, coworker: Union[str, None] = None, **kwargs
):
"""Useful to delegate a specific task to a co-worker passing all necessary context and names."""
coworker = coworker or kwargs.get("co_worker") or kwargs.get("co-worker")
if coworker is not None:
is_list = coworker.startswith("[") and coworker.endswith("]")
if is_list:
coworker = coworker[1:-1].split(",")[0]
return self._execute(coworker, task, context)
def ask_question(self, coworker: str, question: str, context: str):
"""Useful to ask a question, opinion or take from a coworker passing all necessary context and names."""
def ask_question(
self, question: str, context: str, coworker: Union[str, None] = None, **kwargs
):
"""Useful to ask a question, opinion or take from a co-worker passing all necessary context and names."""
coworker = coworker or kwargs.get("co_worker") or kwargs.get("co-worker")
if coworker is not None:
is_list = coworker.startswith("[") and coworker.endswith("]")
if is_list:
coworker = coworker[1:-1].split(",")[0]
return self._execute(coworker, question, context)
def _execute(self, agent, task, context):
@@ -49,7 +63,7 @@ class AgentTools(BaseModel):
for available_agent in self.agents
if available_agent.role.casefold().strip() == agent.casefold().strip()
]
except:
except Exception as _:
return self.i18n.errors("agent_tool_unexsiting_coworker").format(
coworkers="\n".join(
[f"- {agent.role.casefold()}" for agent in self.agents]
@@ -67,6 +81,6 @@ class AgentTools(BaseModel):
task = Task(
description=task,
agent=agent,
expected_output="Your best answer to your coworker asking you this, accounting for the context shared.",
expected_output="Your best answer to your co-worker asking you this, accounting for the context shared.",
)
return agent.execute_task(task, context)

View File

@@ -1,4 +1,5 @@
import ast
from difflib import SequenceMatcher
from textwrap import dedent
from typing import Any, List, Union
@@ -26,13 +27,13 @@ class ToolUsage:
Class that represents the usage of a tool by an agent.
Attributes:
task: Task being executed.
tools_handler: Tools handler that will manage the tool usage.
tools: List of tools available for the agent.
original_tools: Original tools available for the agent before being converted to BaseTool.
tools_description: Description of the tools available for the agent.
tools_names: Names of the tools available for the agent.
function_calling_llm: Language model to be used for the tool usage.
task: Task being executed.
tools_handler: Tools handler that will manage the tool usage.
tools: List of tools available for the agent.
original_tools: Original tools available for the agent before being converted to BaseTool.
tools_description: Description of the tools available for the agent.
tools_names: Names of the tools available for the agent.
function_calling_llm: Language model to be used for the tool usage.
"""
def __init__(
@@ -63,7 +64,7 @@ class ToolUsage:
# Set the maximum parsing attempts for bigger models
if (isinstance(self.function_calling_llm, ChatOpenAI)) and (
self.function_calling_llm.openai_api_base == None
self.function_calling_llm.openai_api_base is None
):
if self.function_calling_llm.model_name in OPENAI_BIGGER_MODELS:
self._max_parsing_attempts = 2
@@ -81,6 +82,8 @@ class ToolUsage:
self._printer.print(content=f"\n\n{error}\n", color="red")
self.task.increment_tools_errors()
return error
# BUG? The code below seems to be unreachable
try:
tool = self._select_tool(calling.tool_name)
except Exception as e:
@@ -88,15 +91,15 @@ class ToolUsage:
self.task.increment_tools_errors()
self._printer.print(content=f"\n\n{error}\n", color="red")
return error
return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}"
return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}" # type: ignore # BUG?: "_use" of "ToolUsage" does not return a value (it only ever returns None)
def _use(
self,
tool_string: str,
tool: BaseTool,
calling: Union[ToolCalling, InstructorToolCalling],
) -> None:
if self._check_tool_repeated_usage(calling=calling):
) -> None: # TODO: Fix this return type
if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
try:
result = self._i18n.errors("task_repeated_usage").format(
tool_names=self.tools_names
@@ -107,15 +110,16 @@ class ToolUsage:
tool_name=tool.name,
attempts=self._run_attempts,
)
result = self._format_result(result=result)
return result
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
return result # type: ignore # Fix the reutrn type of this function
except Exception:
self.task.increment_tools_errors()
result = None
result = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
if self.tools_handler.cache:
result = self.tools_handler.cache.read(
result = self.tools_handler.cache.read( # type: ignore # Incompatible types in assignment (expression has type "str | None", variable has type "str")
tool=calling.tool_name, input=calling.arguments
)
@@ -129,7 +133,7 @@ class ToolUsage:
if calling.arguments:
try:
acceptable_args = tool.args_schema.schema()["properties"].keys()
acceptable_args = tool.args_schema.schema()["properties"].keys() # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "schema"
arguments = {
k: v
for k, v in calling.arguments.items()
@@ -141,7 +145,7 @@ class ToolUsage:
arguments = calling.arguments
result = tool._run(**arguments)
else:
arguments = calling.arguments.values()
arguments = calling.arguments.values() # type: ignore # Incompatible types in assignment (expression has type "dict_values[str, Any]", variable has type "dict[str, Any]")
result = tool._run(*arguments)
else:
result = tool._run()
@@ -157,9 +161,10 @@ class ToolUsage:
).message
self.task.increment_tools_errors()
self._printer.print(content=f"\n\n{error_message}\n", color="red")
return error
return error # type: ignore # No return value expected
self.task.increment_tools_errors()
return self.use(calling=calling, tool_string=tool_string)
return self.use(calling=calling, tool_string=tool_string) # type: ignore # No return value expected
if self.tools_handler:
should_cache = True
@@ -168,9 +173,9 @@ class ToolUsage:
)
if (
hasattr(original_tool, "cache_function")
and original_tool.cache_function
and original_tool.cache_function # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
):
should_cache = original_tool.cache_function(
should_cache = original_tool.cache_function( # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
calling.arguments, result
)
@@ -184,13 +189,13 @@ class ToolUsage:
tool_name=tool.name,
attempts=self._run_attempts,
)
result = self._format_result(result=result)
return result
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
return result # type: ignore # No return value expected
def _format_result(self, result: Any) -> None:
self.task.used_tools += 1
if self._should_remember_format():
result = self._remember_format(result=result)
if self._should_remember_format(): # type: ignore # "_should_remember_format" of "ToolUsage" does not return a value (it only ever returns None)
result = self._remember_format(result=result) # type: ignore # "_remember_format" of "ToolUsage" does not return a value (it only ever returns None)
return result
def _should_remember_format(self) -> None:
@@ -201,26 +206,39 @@ class ToolUsage:
result += "\n\n" + self._i18n.slice("tools").format(
tools=self.tools_description, tool_names=self.tools_names
)
return result
return result # type: ignore # No return value expected
def _check_tool_repeated_usage(
self, calling: Union[ToolCalling, InstructorToolCalling]
) -> None:
if not self.tools_handler:
return False
return False # type: ignore # No return value expected
if last_tool_usage := self.tools_handler.last_used_tool:
return (calling.tool_name == last_tool_usage.tool_name) and (
return (calling.tool_name == last_tool_usage.tool_name) and ( # type: ignore # No return value expected
calling.arguments == last_tool_usage.arguments
)
def _select_tool(self, tool_name: str) -> BaseTool:
for tool in self.tools:
if tool.name.lower().strip() == tool_name.lower().strip():
order_tools = sorted(
self.tools,
key=lambda tool: SequenceMatcher(
None, tool.name.lower().strip(), tool_name.lower().strip()
).ratio(),
reverse=True,
)
for tool in order_tools:
if (
tool.name.lower().strip() == tool_name.lower().strip()
or SequenceMatcher(
None, tool.name.lower().strip(), tool_name.lower().strip()
).ratio()
> 0.85
):
return tool
self.task.increment_tools_errors()
if tool_name and tool_name != "":
raise Exception(
f"Action '{tool_name}' don't exist, these are the only available Actions: {self.tools_description}"
f"Action '{tool_name}' don't exist, these are the only available Actions:\n {self.tools_description}"
)
else:
raise Exception(
@@ -247,7 +265,7 @@ class ToolUsage:
return "\n--\n".join(descriptions)
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base == None
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None
def _tool_calling(
self, tool_string: str
@@ -260,17 +278,17 @@ class ToolUsage:
else ToolCalling
)
converter = Converter(
text=f"Only tools available:\n###\n{self._render()}\n\nReturn a valid schema for the tool, the tool name must be exactly equal one of the options, use this text to inform the valid ouput schema:\n\n{tool_string}```",
text=f"Only tools available:\n###\n{self._render()}\n\nReturn a valid schema for the tool, the tool name must be exactly equal one of the options, use this text to inform the valid output schema:\n\n{tool_string}```",
llm=self.function_calling_llm,
model=model,
instructions=dedent(
"""\
The schema should have the following structure, only two keys:
- tool_name: str
- arguments: dict (with all arguments being passed)
The schema should have the following structure, only two keys:
- tool_name: str
- arguments: dict (with all arguments being passed)
Example:
{"tool_name": "tool name", "arguments": {"arg_name1": "value", "arg_name2": 2}}""",
Example:
{"tool_name": "tool name", "arguments": {"arg_name1": "value", "arg_name2": 2}}""",
),
max_attemps=1,
)
@@ -282,16 +300,17 @@ class ToolUsage:
tool_name = self.action.tool
tool = self._select_tool(tool_name)
try:
arguments = ast.literal_eval(self.action.tool_input)
tool_input = self._validate_tool_input(self.action.tool_input)
arguments = ast.literal_eval(tool_input)
except Exception:
return ToolUsageErrorException(
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
f'{self._i18n.errors("tool_arguments_error")}'
)
if not isinstance(arguments, dict):
return ToolUsageErrorException(
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
f'{self._i18n.errors("tool_arguments_error")}'
)
calling = ToolCalling(
calling = ToolCalling( # type: ignore # Unexpected keyword argument "log" for "ToolCalling"
tool_name=tool.name,
arguments=arguments,
log=tool_string,
@@ -302,9 +321,60 @@ class ToolUsage:
self._telemetry.tool_usage_error(llm=self.function_calling_llm)
self.task.increment_tools_errors()
self._printer.print(content=f"\n\n{e}\n", color="red")
return ToolUsageErrorException(
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
f'{self._i18n.errors("tool_usage_error").format(error=e)}\nMoving on then. {self._i18n.slice("format").format(tool_names=self.tools_names)}'
)
return self._tool_calling(tool_string)
return calling
def _validate_tool_input(self, tool_input: str) -> str:
try:
ast.literal_eval(tool_input)
return tool_input
except Exception:
# Clean and ensure the string is properly enclosed in braces
tool_input = tool_input.strip()
if not tool_input.startswith("{"):
tool_input = "{" + tool_input
if not tool_input.endswith("}"):
tool_input += "}"
# Manually split the input into key-value pairs
entries = tool_input.strip("{} ").split(",")
formatted_entries = []
for entry in entries:
if ":" not in entry:
continue # Skip malformed entries
key, value = entry.split(":", 1)
# Remove extraneous white spaces and quotes, replace single quotes
key = key.strip().strip('"').replace("'", '"')
value = value.strip()
# Handle replacement of single quotes at the start and end of the value string
if value.startswith("'") and value.endswith("'"):
value = value[1:-1] # Remove single quotes
value = (
'"' + value.replace('"', '\\"') + '"'
) # Re-encapsulate with double quotes
elif value.isdigit(): # Check if value is a digit, hence integer
formatted_value = value
elif value.lower() in [
"true",
"false",
"null",
]: # Check for boolean and null values
formatted_value = value.lower()
else:
# Assume the value is a string and needs quotes
formatted_value = '"' + value.replace('"', '\\"') + '"'
# Rebuild the entry with proper quoting
formatted_entry = f'"{key}": {formatted_value}'
formatted_entries.append(formatted_entry)
# Reconstruct the JSON string
new_json_string = "{" + ", ".join(formatted_entries) + "}"
return new_json_string

View File

@@ -6,12 +6,12 @@
},
"slices": {
"observation": "\nObservation",
"task": "\nCurrent Task: {input}\n\nBegin! This is VERY important to you, use the tools available and give your best Final Answer, your job depends on it!\n\nThought: ",
"task": "\nCurrent Task: {input}\n\nBegin! This is VERY important to you, use the tools available and give your best Final Answer, your job depends on it!\n\nThought:",
"memory": "\n\n# Useful context: \n{memory}",
"role_playing": "You are {role}. {backstory}\nYour personal goal is: {goal}",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple a python dictionary using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple a python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"no_tools": "To give my best complete final answer to the task use the exact following format:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\nYour final answer must be the great and the most complete as possible, it must be outcome described.\n\nI MUST use these formats, my job depends on it!",
"format": "I MUST either use a tool (use one at time) OR give my best final answer. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\nYour final answer must be the great and the most complete as possible, it must be outcome described\n\n ",
"format": "I MUST either use a tool (use one at time) OR give my best final answer. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\nYour final answer must be the great and the most complete as possible, it must be outcome described\n\n ",
"final_answer_format": "If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\n\n",
"format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task\nYour final answer must be the great and the most complete as possible, it must be outcome described\n\n",
"task_with_context": "{task}\n\nThis is the context you're working with:\n{context}",
@@ -20,7 +20,6 @@
"getting_input": "This is the agent final answer: {final_answer}\nPlease provide a feedback: "
},
"errors": {
"unexpected_format": "\nSorry, I didn't use the expected format, I MUST either use a tool (use one at time) OR give my best final answer.\n",
"force_final_answer": "Tool won't be use because it's time to give your final answer. Don't use tools and just your absolute BEST Final answer.",
"agent_tool_unexsiting_coworker": "\nError executing tool. Co-worker mentioned not found, it must to be one of the following options:\n{coworkers}\n",
"task_repeated_usage": "I tried reusing the same input, I must stop using this action input. I'll try something else instead.\n\n",
@@ -30,7 +29,7 @@
"tool_usage_exception": "I encountered an error while trying to use the tool. This was the error: {error}.\n Tool {tool} accepts these inputs: {tool_inputs}"
},
"tools": {
"delegate_work": "Delegate a specific task to one of the following co-workers: {coworkers}\nThe input to this tool should be the coworker, the task you want them to do, and ALL necessary context to exectue the task, they know nothing about the task, so share absolute everything you know, don't reference things but instead explain them.",
"ask_question": "Ask a specific question to one of the following co-workers: {coworkers}\nThe input to this tool should be the coworker, the question you have for them, and ALL necessary context to ask the question properly, they know nothing about the question, so share absolute everything you know, don't reference things but instead explain them."
"delegate_work": "Delegate a specific task to one of the following co-workers: {coworkers}\nThe input to this tool should be the co-worker, the task you want them to do, and ALL necessary context to execute the task, they know nothing about the task, so share absolute everything you know, don't reference things but instead explain them.",
"ask_question": "Ask a specific question to one of the following co-workers: {coworkers}\nThe input to this tool should be the co-worker, the question you have for them, and ALL necessary context to ask the question properly, they know nothing about the question, so share absolute everything you know, don't reference things but instead explain them."
}
}

View File

@@ -6,3 +6,4 @@ from .printer import Printer
from .prompts import Prompts
from .rpm_controller import RPMController
from .fileHandler import FileHandler
from .parser import YamlParser

View File

@@ -83,5 +83,5 @@ class Converter(BaseModel):
)
return new_prompt | self.llm | parser
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base == None
def _is_gpt(self, llm) -> bool: # type: ignore # BUG? Name "_is_gpt" defined on line 20 hides name from outer scope
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None

View File

@@ -58,4 +58,4 @@ class TaskEvaluator:
return converter.to_pydantic()
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base == None
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None

View File

@@ -2,44 +2,36 @@ import json
import os
from typing import Dict, Optional
from pydantic import BaseModel, Field, PrivateAttr, ValidationError, model_validator
from pydantic import BaseModel, Field, PrivateAttr, model_validator
class I18N(BaseModel):
_translations: Dict[str, Dict[str, str]] = PrivateAttr()
language_file: Optional[str] = Field(
_prompts: Dict[str, Dict[str, str]] = PrivateAttr()
prompt_file: Optional[str] = Field(
default=None,
description="Path to the translation file to load",
)
language: Optional[str] = Field(
default="en",
description="Language used to load translations",
description="Path to the prompt_file file to load",
)
@model_validator(mode="after")
def load_translation(self) -> "I18N":
"""Load translations from a JSON file based on the specified language."""
def load_prompts(self) -> "I18N":
"""Load prompts from a JSON file."""
try:
if self.language_file:
with open(self.language_file, "r") as f:
self._translations = json.load(f)
if self.prompt_file:
with open(self.prompt_file, "r") as f:
self._prompts = json.load(f)
else:
dir_path = os.path.dirname(os.path.realpath(__file__))
prompts_path = os.path.join(
dir_path, f"../translations/{self.language}.json"
)
prompts_path = os.path.join(dir_path, "../translations/en.json")
with open(prompts_path, "r") as f:
self._translations = json.load(f)
self._prompts = json.load(f)
except FileNotFoundError:
raise ValidationError(
f"Translation file for language '{self.language}' not found."
)
raise Exception(f"Prompt file '{self.prompt_file}' not found.")
except json.JSONDecodeError:
raise ValidationError(f"Error decoding JSON from the prompts file.")
raise Exception("Error decoding JSON from the prompts file.")
if not self._translations:
self._translations = {}
if not self._prompts:
self._prompts = {}
return self
@@ -54,6 +46,6 @@ class I18N(BaseModel):
def retrieve(self, kind, key) -> str:
try:
return self._translations[kind][key]
except:
raise ValidationError(f"Translation for '{kind}':'{key}' not found.")
return self._prompts[kind][key]
except Exception as _:
raise Exception(f"Prompt for '{kind}':'{key}' not found.")

View File

@@ -0,0 +1,17 @@
import re
class YamlParser:
def parse(file):
content = file.read()
# Replace single { and } with doubled ones, while leaving already doubled ones intact and the other special characters {# and {%
modified_content = re.sub(r"(?<!\{){(?!\{)(?!\#)(?!\%)", "{{", content)
modified_content = re.sub(
r"(?<!\})(?<!\%)(?<!\#)\}(?!})", "}}", modified_content
)
# Check for 'context:' not followed by '[' and raise an error
if re.search(r"context:(?!\s*\[)", modified_content):
raise ValueError(
"Context is currently only supported in code when creating a task. Please use the 'context' key in the task configuration."
)
return modified_content

View File

@@ -1,3 +1,4 @@
import os
from pathlib import Path
import appdirs
@@ -13,6 +14,11 @@ def db_storage_path():
def get_project_directory_name():
cwd = Path.cwd()
project_directory_name = cwd.name
return project_directory_name
project_directory_name = os.environ.get("CREWAI_STORAGE_DIR")
if project_directory_name:
return project_directory_name
else:
cwd = Path.cwd()
project_directory_name = cwd.name
return project_directory_name

View File

@@ -1,4 +1,4 @@
from typing import Any, ClassVar
from typing import Any, ClassVar, Optional
from langchain.prompts import BasePromptTemplate, PromptTemplate
from pydantic import BaseModel, Field
@@ -7,16 +7,15 @@ from crewai.utilities import I18N
class Prompts(BaseModel):
"""Manages and generates prompts for a generic agent with support for different languages."""
"""Manages and generates prompts for a generic agent."""
i18n: I18N = Field(default=I18N())
tools: list[Any] = Field(default=[])
system_template: Optional[str] = None
prompt_template: Optional[str] = None
response_template: Optional[str] = None
SCRATCHPAD_SLICE: ClassVar[str] = "\n{agent_scratchpad}"
def task_execution_without_tools(self) -> BasePromptTemplate:
"""Generate a prompt for task execution without tools components."""
return self._build_prompt(["role_playing", "task"])
def task_execution(self) -> BasePromptTemplate:
"""Generate a standard prompt for task execution."""
slices = ["role_playing"]
@@ -24,12 +23,42 @@ class Prompts(BaseModel):
slices.append("tools")
else:
slices.append("no_tools")
slices.append("task")
return self._build_prompt(slices)
def _build_prompt(self, components: list[str]) -> BasePromptTemplate:
slices.append("task")
if not self.system_template and not self.prompt_template:
return self._build_prompt(slices)
else:
return self._build_prompt(
slices,
self.system_template,
self.prompt_template,
self.response_template,
)
def _build_prompt(
self,
components: list[str],
system_template=None,
prompt_template=None,
response_template=None,
) -> BasePromptTemplate:
"""Constructs a prompt string from specified components."""
prompt_parts = [self.i18n.slice(component) for component in components]
prompt_parts.append(self.SCRATCHPAD_SLICE)
prompt = PromptTemplate.from_template("".join(prompt_parts))
if not system_template and not prompt_template:
prompt_parts = [self.i18n.slice(component) for component in components]
prompt_parts.append(self.SCRATCHPAD_SLICE)
prompt = PromptTemplate.from_template("".join(prompt_parts))
else:
prompt_parts = [
self.i18n.slice(component)
for component in components
if component != "task"
]
system = system_template.replace("{{ .System }}", "".join(prompt_parts))
prompt = prompt_template.replace(
"{{ .Prompt }}",
"".join([self.i18n.slice("task"), self.SCRATCHPAD_SLICE]),
)
response = response_template.split("{{ .Response }}")[0]
prompt = PromptTemplate.from_template(f"{system}\n{prompt}\n{response}")
return prompt

View File

@@ -22,7 +22,7 @@ class TokenProcess:
def sum_successful_requests(self, requests: int):
self.successful_requests = self.successful_requests + requests
def get_summary(self) -> str:
def get_summary(self) -> Dict[str, Any]:
return {
"total_tokens": self.total_tokens,
"prompt_tokens": self.prompt_tokens,
@@ -42,12 +42,12 @@ class TokenCalcHandler(BaseCallbackHandler):
def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
) -> None:
if "gpt" in self.model:
try:
encoding = tiktoken.encoding_for_model(self.model)
else:
except KeyError:
encoding = tiktoken.get_encoding("cl100k_base")
if self.token_cost_process == None:
if self.token_cost_process is None:
return
for prompt in prompts:

View File

@@ -31,8 +31,8 @@ def test_agent_default_values():
assert isinstance(agent.llm, ChatOpenAI)
assert agent.llm.model_name == "gpt-4"
assert agent.llm.temperature == 0.7
assert agent.llm.verbose == False
assert agent.allow_delegation == True
assert agent.llm.verbose is False
assert agent.allow_delegation is True
def test_custom_llm():
@@ -751,9 +751,10 @@ def test_agent_definition_based_on_dict():
assert agent.role == "test role"
assert agent.goal == "test goal"
assert agent.backstory == "test backstory"
assert agent.verbose == True
assert agent.verbose is True
assert agent.tools == []
# test for human input
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_human_input():
@@ -780,6 +781,7 @@ def test_agent_human_input():
mock_human_input.assert_called_once()
assert output == "Hello"
def test_interpolate_inputs():
agent = Agent(
role="{topic} specialist",
@@ -797,3 +799,46 @@ def test_interpolate_inputs():
assert agent.goal == "Figure stuff out"
assert agent.backstory == "I am the master of nothing"
def test_system_and_prompt_template():
agent = Agent(
role="{topic} specialist",
goal="Figure {goal} out",
backstory="I am the master of {role}",
system_template="""<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>""",
prompt_template="""<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>""",
response_template="""<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>""",
)
template = agent.agent_executor.agent.dict()["runnable"]["middle"][0]["template"]
assert (
template
== """<|start_header_id|>system<|end_header_id|>
You are {role}. {backstory}
Your personal goal is: {goal}To give my best complete final answer to the task use the exact following format:
Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.
Your final answer must be the great and the most complete as possible, it must be outcome described.
I MUST use these formats, my job depends on it!<|eot_id|>
<|start_header_id|>user<|end_header_id|>
Current Task: {input}
Begin! This is VERY important to you, use the tools available and give your best Final Answer, your job depends on it!
Thought:
{agent_scratchpad}<|eot_id|>
<|start_header_id|>assistant<|end_header_id|>
"""
)

View File

@@ -28,6 +28,20 @@ def test_delegate_work():
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_delegate_work_with_wrong_co_worker_variable():
result = tools.delegate_work(
co_worker="researcher",
task="share your take on AI Agents",
context="I heard you hate them",
)
assert (
result
== "AI Agents are essentially computer programs that are designed to perform tasks autonomously, with the ability to adapt and learn from their environment. These tasks range from simple ones such as setting alarms, to more complex ones like diagnosing diseases or driving cars. AI agents have the potential to revolutionize many industries, making processes more efficient and accurate. \n\nHowever, like any technology, AI agents have their downsides. They can be susceptible to biases based on the data they're trained on and they can also raise privacy concerns. Moreover, the widespread adoption of AI agents could result in significant job displacement in certain industries.\n\nDespite these concerns, it's important to note that the development and use of AI agents are heavily dependent on human decisions and policies. Therefore, the key to harnessing the benefits of AI agents while mitigating the risks lies in responsible and thoughtful development and implementation.\n\nWhether one 'loves' or 'hates' AI agents often comes down to individual perspectives and experiences. But as a researcher, it is my job to provide balanced and factual information, so I hope this explanation helps you understand better what AI Agents are and the implications they have."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_ask_question():
result = tools.ask_question(
@@ -41,6 +55,46 @@ def test_ask_question():
== "As an AI researcher, I don't have personal feelings or emotions like love or hate. However, I recognize the importance of AI Agents in today's technological landscape. They have the potential to greatly enhance our lives and make tasks more efficient. At the same time, it is crucial to consider the ethical implications and societal impacts that come with their use. My role is to provide objective research and analysis on these topics."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_ask_question_with_wrong_co_worker_variable():
result = tools.ask_question(
co_worker="researcher",
question="do you hate AI Agents?",
context="I heard you LOVE them",
)
assert (
result
== "No, I don't hate AI agents. In fact, I find them quite fascinating. They are powerful tools that can greatly assist in various tasks, including my research. As a technology researcher, AI and AI agents are subjects of interest to me due to their potential in advancing our understanding and capabilities in various fields. My supposed love for them stems from this professional interest and the potential they hold."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_delegate_work_withwith_coworker_as_array():
result = tools.delegate_work(
co_worker="[researcher]",
task="share your take on AI Agents",
context="I heard you hate them",
)
assert (
result
== "AI Agents are software entities which operate in an environment to achieve a particular goal. They can perceive their environment, reason about it, and take actions to fulfill their objectives. This includes everything from chatbots to self-driving cars. They are designed to act autonomously to a certain extent and are capable of learning from their experiences to improve their performance over time.\n\nDespite some people's fears or dislikes, AI Agents are not inherently good or bad. They are tools, and like any tool, their value depends on how they are used. For instance, AI Agents can be used to automate repetitive tasks, provide customer support, or analyze vast amounts of data far more quickly and accurately than a human could. They can also be used in ways that invade privacy or replace jobs, which is often where the apprehension comes from.\n\nThe key is to create regulations and ethical guidelines for the use of AI Agents, and to continue researching and developing them in a way that maximizes their benefits and minimizes their potential harm. From a research perspective, there's a lot of potential in AI Agents, and it's a fascinating field to be a part of."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_ask_question_with_coworker_as_array():
result = tools.ask_question(
co_worker="[researcher]",
question="do you hate AI Agents?",
context="I heard you LOVE them",
)
assert (
result
== "I don't hate or love AI agents. My passion lies in understanding them, researching about their capabilities, implications, and potential for development. As a researcher, my feelings toward AI are more of fascination and interest rather than personal love or hate."
)
def test_delegate_work_to_wrong_agent():
result = tools.ask_question(

View File

@@ -0,0 +1,427 @@
interactions:
- request:
body: '{"messages": [{"role": "user", "content": "You are researcher. You''re
an expert researcher, specialized in technology\nYour personal goal is: make
the best research and analysis on content about AI and AI agentsTo give my best
complete final answer to the task use the exact following format:\n\nThought:
I now can give a great answer\nFinal Answer: my best complete final answer to
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described.\n\nI MUST use these formats, my job depends on
it!\nCurrent Task: do you hate AI Agents?\n\nThis is the expect criteria for
your final answer: Your best answer to your co-worker asking you this, accounting
for the context shared. \n you MUST return the actual complete content as the
final answer, not a summary.\n\nThis is the context you''re working with:\nI
heard you LOVE them\n\nBegin! This is VERY important to you, use the tools available
and give your best Final Answer, your job depends on it!\n\nThought:\n"}], "model":
"gpt-4", "n": 1, "stop": ["\nObservation"], "stream": true, "temperature": 0.7}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, br
connection:
- keep-alive
content-length:
- '1103'
content-type:
- application/json
cookie:
- _cfuvid=r7d0l_hEOM639ffTY.owt.2ZeXQN7IgsM0JmFM6FEiE-1715576040584-0.0.1.1-604800000;
__cf_bm=bHXznnX6IEnPWC4UFqVw22jiTLBlLvClKsTW4F99UPc-1715613132-1.0.1.1-5k2TYkm6.lsjrA1MkQ4uD2GxUGKPmwNVeYL_sKTpAPJ_trVvN3.uNZS4HljKfVPlku1XDNCYfU4y43hlt3e.RQ
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.25.1
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.25.1
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.11.7
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: 'data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"As"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
a"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
researcher"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
specialized"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
in"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
technology"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
specifically"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
AI"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
and"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
AI"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
agents"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
my"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
personal"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
feelings"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
towards"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
them"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
are"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
not"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
based"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
on"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
emotion"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
but"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
on"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
professional"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
interest"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
and"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
intellectual"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
curiosity"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
\n\n"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Final"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
Answer"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
I"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
don"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"''t"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
hate"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
or"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
love"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
AI"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
agents"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
My"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
passion"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
lies"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
in"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
understanding"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
them"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
researching"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
about"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
their"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
capabilities"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
implications"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
and"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
potential"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
for"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
development"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
As"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
a"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
researcher"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
my"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
feelings"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
toward"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
AI"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
are"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
more"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
of"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
fascination"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
and"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
interest"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
rather"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
than"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
personal"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
love"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
or"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"
hate"},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-9ORdkG4FJphn6RbqXUgZlUbRtXBIO","object":"chat.completion.chunk","created":1715613148,"model":"gpt-4-0613","system_fingerprint":null,"choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
data: [DONE]
'
headers:
CF-Cache-Status:
- DYNAMIC
CF-RAY:
- 883396429922624c-GRU
Connection:
- keep-alive
Content-Type:
- text/event-stream; charset=utf-8
Date:
- Mon, 13 May 2024 15:12:29 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
alt-svc:
- h3=":443"; ma=86400
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '514'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=15724800; includeSubDomains
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '300000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '299745'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 50ms
x-request-id:
- req_5a62449ef9052c3a350f1b47f268bbcc
status:
code: 200
message: OK
version: 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [Researcher]\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to exectue the task, they
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question
@@ -1030,7 +1030,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [Researcher]\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to exectue the task, they
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question

View File

@@ -6,7 +6,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -326,7 +326,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -351,7 +351,7 @@ interactions:
Delegate work to co-worker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following co-workers: [''test role2'']\nThe input
to this tool should be the coworker, the task you want them to do, and ALL necessary
context to exectue the task, they know nothing about the task, so share absolute
context to execute the task, they know nothing about the task, so share absolute
everything you know, don''t reference things but instead explain them.\nAsk
question to co-worker: Ask question to co-worker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following co-workers:
@@ -1027,7 +1027,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -1052,7 +1052,7 @@ interactions:
Delegate work to co-worker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following co-workers: [''test role2'']\nThe input
to this tool should be the coworker, the task you want them to do, and ALL necessary
context to exectue the task, they know nothing about the task, so share absolute
context to execute the task, they know nothing about the task, so share absolute
everything you know, don''t reference things but instead explain them.\nAsk
question to co-worker: Ask question to co-worker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following co-workers:
@@ -1236,7 +1236,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -1485,7 +1485,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -2490,7 +2490,7 @@ interactions:
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:

View File

@@ -67,7 +67,7 @@ interactions:
work to co-worker: Delegate work to co-worker(coworker: str, task: str, context:
str) - Delegate a specific task to one of the following co-workers: [''Researcher'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
@@ -442,7 +442,7 @@ interactions:
work to co-worker: Delegate work to co-worker(coworker: str, task: str, context:
str) - Delegate a specific task to one of the following co-workers: [''Researcher'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to exectue the task, they know nothing about the task,
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:

View File

@@ -324,7 +324,7 @@ interactions:
Name: learn_about_ai\nTool Description: learn_about_AI(topic) -> float - Useful
for when you need to learn about AI to write an paragraph about it.\nTool Arguments:
{''topic'': {}}\n\nReturn a valid schema for the tool, the tool name must be
exactly equal one of the options, use this text to inform the valid ouput schema:\n\nThought:
exactly equal one of the options, use this text to inform the valid output schema:\n\nThought:
Before I can write an amazing article about AI, I need to understand the basics
of AI, its applications, and ethical considerations to ensure that the content
is well-rounded and informative.\n\nAction: learn_about_AI\nAction Input: {\"topic\":
@@ -854,7 +854,7 @@ interactions:
Name: learn_about_ai\nTool Description: learn_about_AI(topic) -> float - Useful
for when you need to learn about AI to write an paragraph about it.\nTool Arguments:
{''topic'': {}}\n\nReturn a valid schema for the tool, the tool name must be
exactly equal one of the options, use this text to inform the valid ouput schema:\n\nThought:
exactly equal one of the options, use this text to inform the valid output schema:\n\nThought:
Now that I know AI is a broad field, I should learn specifically about its applications
to provide concrete examples in my article.\n\nAction: learn_about_AI\nAction
Input: {\"topic\": \"applications of AI\"}```"}, {"role": "system", "content":
@@ -1325,7 +1325,7 @@ interactions:
Name: learn_about_ai\nTool Description: learn_about_AI(topic) -> float - Useful
for when you need to learn about AI to write an paragraph about it.\nTool Arguments:
{''topic'': {}}\n\nReturn a valid schema for the tool, the tool name must be
exactly equal one of the options, use this text to inform the valid ouput schema:\n\nThought:
exactly equal one of the options, use this text to inform the valid output schema:\n\nThought:
It seems there was an error with the action input. I need to correct the format
to ensure the tool works properly for gathering information on the applications
of AI.\n\nAction: learn_about_AI\nAction Input: {\"topic\": \"applications of
@@ -1807,7 +1807,7 @@ interactions:
Name: learn_about_ai\nTool Description: learn_about_AI(topic) -> float - Useful
for when you need to learn about AI to write an paragraph about it.\nTool Arguments:
{''topic'': {}}\n\nReturn a valid schema for the tool, the tool name must be
exactly equal one of the options, use this text to inform the valid ouput schema:\n\nThought:
exactly equal one of the options, use this text to inform the valid output schema:\n\nThought:
Having understood that AI is a broad field, and after the corrected attempt
to learn about its applications, I realize I need more detailed insights into
ethical considerations around AI to make sure the article covers a balanced

File diff suppressed because it is too large Load Diff

View File

@@ -86,7 +86,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Senior Writer'']\nThe input to this tool should be the coworker,
the task you want them to do, and ALL necessary context to exectue the task,
the task you want them to do, and ALL necessary context to execute the task,
they know nothing about the task, so share absolute everything you know, don''t
reference things but instead explain them.\nAsk question to co-worker: Ask question
to co-worker(coworker: str, question: str, context: str) - Ask a specific question
@@ -1716,7 +1716,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Senior Writer'']\nThe input to this tool should be the coworker,
the task you want them to do, and ALL necessary context to exectue the task,
the task you want them to do, and ALL necessary context to execute the task,
they know nothing about the task, so share absolute everything you know, don''t
reference things but instead explain them.\nAsk question to co-worker: Ask question
to co-worker(coworker: str, question: str, context: str) - Ask a specific question

View File

@@ -67,7 +67,7 @@ interactions:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Researcher'', ''Senior Writer'']\nThe input to this tool should
be the coworker, the task you want them to do, and ALL necessary context to
exectue the task, they know nothing about the task, so share absolute everything
execute the task, they know nothing about the task, so share absolute everything
you know, don''t reference things but instead explain them.\nAsk question to
co-worker: Ask question to co-worker(coworker: str, question: str, context:
str) - Ask a specific question to one of the following co-workers: [''Researcher'',
@@ -2448,7 +2448,7 @@ interactions:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Researcher'', ''Senior Writer'']\nThe input to this tool should
be the coworker, the task you want them to do, and ALL necessary context to
exectue the task, they know nothing about the task, so share absolute everything
execute the task, they know nothing about the task, so share absolute everything
you know, don''t reference things but instead explain them.\nAsk question to
co-worker: Ask question to co-worker(coworker: str, question: str, context:
str) - Ask a specific question to one of the following co-workers: [''Researcher'',
@@ -5367,7 +5367,7 @@ interactions:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Researcher'', ''Senior Writer'']\nThe input to this tool should
be the coworker, the task you want them to do, and ALL necessary context to
exectue the task, they know nothing about the task, so share absolute everything
execute the task, they know nothing about the task, so share absolute everything
you know, don''t reference things but instead explain them.\nAsk question to
co-worker: Ask question to co-worker(coworker: str, question: str, context:
str) - Ask a specific question to one of the following co-workers: [''Researcher'',

View File

@@ -60,7 +60,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Scorer'']\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to exectue the task, they
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question
@@ -3200,7 +3200,7 @@ interactions:
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [''Scorer'']\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to exectue the task, they
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question

File diff suppressed because it is too large Load Diff

59
tests/cli/cli_test.py Normal file
View File

@@ -0,0 +1,59 @@
from unittest import mock
import pytest
from click.testing import CliRunner
from crewai.cli.cli import train, version
@pytest.fixture
def runner():
return CliRunner()
@mock.patch("crewai.cli.cli.train_crew")
def test_train_default_iterations(train_crew, runner):
result = runner.invoke(train)
train_crew.assert_called_once_with(5)
assert result.exit_code == 0
assert "Training the crew for 5 iterations" in result.output
@mock.patch("crewai.cli.cli.train_crew")
def test_train_custom_iterations(train_crew, runner):
result = runner.invoke(train, ["--n_iterations", "10"])
train_crew.assert_called_once_with(10)
assert result.exit_code == 0
assert "Training the crew for 10 iterations" in result.output
@mock.patch("crewai.cli.cli.train_crew")
def test_train_invalid_string_iterations(train_crew, runner):
result = runner.invoke(train, ["--n_iterations", "invalid"])
train_crew.assert_not_called()
assert result.exit_code == 2
assert (
"Usage: train [OPTIONS]\nTry 'train --help' for help.\n\nError: Invalid value for '-n' / '--n_iterations': 'invalid' is not a valid integer.\n"
in result.output
)
def test_version_command(runner):
result = runner.invoke(version)
assert result.exit_code == 0
assert "crewai version:" in result.output
def test_version_command_with_tools(runner):
result = runner.invoke(version, ["--tools"])
assert result.exit_code == 0
assert "crewai version:" in result.output
assert (
"crewai tools version:" in result.output
or "crewai tools not installed" in result.output
)

View File

@@ -0,0 +1,87 @@
import subprocess
from unittest import mock
from crewai.cli.train_crew import train_crew
@mock.patch("crewai.cli.train_crew.subprocess.run")
def test_train_crew_positive_iterations(mock_subprocess_run):
# Arrange
n_iterations = 5
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["poetry", "run", "train", str(n_iterations)],
returncode=0,
stdout="Success",
stderr="",
)
# Act
train_crew(n_iterations)
# Assert
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", str(n_iterations)],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.train_crew.click")
def test_train_crew_zero_iterations(click):
train_crew(0)
click.echo.assert_called_once_with(
"An unexpected error occurred: The number of iterations must be a positive integer.",
err=True,
)
@mock.patch("crewai.cli.train_crew.click")
def test_train_crew_negative_iterations(click):
train_crew(-2)
click.echo.assert_called_once_with(
"An unexpected error occurred: The number of iterations must be a positive integer.",
err=True,
)
@mock.patch("crewai.cli.train_crew.click")
@mock.patch("crewai.cli.train_crew.subprocess.run")
def test_train_crew_called_process_error(mock_subprocess_run, click):
n_iterations = 5
mock_subprocess_run.side_effect = subprocess.CalledProcessError(
returncode=1,
cmd=["poetry", "run", "train", str(n_iterations)],
output="Error",
stderr="Some error occurred",
)
train_crew(n_iterations)
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", "5"], capture_output=False, text=True, check=True
)
click.echo.assert_has_calls(
[
mock.call.echo(
"An error occurred while training the crew: Command '['poetry', 'run', 'train', '5']' returned non-zero exit status 1.",
err=True,
),
mock.call.echo("Error", err=True),
]
)
@mock.patch("crewai.cli.train_crew.click")
@mock.patch("crewai.cli.train_crew.subprocess.run")
def test_train_crew_unexpected_exception(mock_subprocess_run, click):
# Arrange
n_iterations = 5
mock_subprocess_run.side_effect = Exception("Unexpected error")
train_crew(n_iterations)
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", "5"], capture_output=False, text=True, check=True
)
click.echo.assert_called_once_with(
"An unexpected error occurred: Unexpected error", err=True
)

View File

@@ -437,6 +437,7 @@ def test_async_task_execution():
process=Process.sequential,
tasks=[list_ideas, list_important_history, write_article],
)
output = TaskOutput(description="A 4 paragraph article about AI.", raw_output="ok")
with patch.object(Agent, "execute_task") as execute:
execute.return_value = "ok"
@@ -444,13 +445,11 @@ def test_async_task_execution():
thread = threading.Thread(target=lambda: None, args=()).start()
start.return_value = thread
with patch.object(threading.Thread, "join", wraps=thread.join()) as join:
list_ideas.output = TaskOutput(
description="A 4 paragraph article about AI.", raw_output="ok"
)
list_important_history.output = TaskOutput(
description="A 4 paragraph article about AI.", raw_output="ok"
)
list_ideas.output = output
list_important_history.output = output
crew.kickoff()
start.assert_called()
join.assert_called()
@@ -648,10 +647,10 @@ def test_agent_usage_metrics_are_captured_for_sequential_process():
result = crew.kickoff()
assert result == "Howdy!"
assert crew.usage_metrics == {
"completion_tokens": 51,
"prompt_tokens": 483,
"successful_requests": 3,
"total_tokens": 534,
"completion_tokens": 17,
"prompt_tokens": 160,
"successful_requests": 1,
"total_tokens": 177,
}
@@ -678,10 +677,10 @@ def test_agent_usage_metrics_are_captured_for_hierarchical_process():
result = crew.kickoff()
assert result == '"Howdy!"'
assert crew.usage_metrics == {
"total_tokens": 2592,
"prompt_tokens": 2048,
"completion_tokens": 544,
"successful_requests": 6,
"total_tokens": 1666,
"prompt_tokens": 1383,
"completion_tokens": 283,
"successful_requests": 3,
}
@@ -698,6 +697,8 @@ def test_crew_inputs_interpolate_both_agents_and_tasks():
)
crew = Crew(agents=[agent], tasks=[task], inputs={"topic": "AI", "points": 5})
inputs = {"topic": "AI", "points": 5}
crew._interpolate_inputs(inputs=inputs) # Manual call for now
assert crew.tasks[0].description == "Give me an analysis around AI."
assert crew.tasks[0].expected_output == "5 bullet points about AI."
@@ -706,7 +707,7 @@ def test_crew_inputs_interpolate_both_agents_and_tasks():
assert crew.agents[0].backstory == "You have a lot of experience with AI."
def test_crew_inputs_interpolate_both_agents_and_tasks():
def test_crew_inputs_interpolate_both_agents_and_tasks_diff():
from unittest.mock import patch
agent = Agent(
@@ -828,9 +829,7 @@ def test_tools_with_custom_caching():
with patch.object(
CacheHandler, "add", wraps=crew._cache_handler.add
) as add_to_cache:
with patch.object(
CacheHandler, "read", wraps=crew._cache_handler.read
) as read_from_cache:
with patch.object(CacheHandler, "read", wraps=crew._cache_handler.read) as _:
result = crew.kickoff()
add_to_cache.assert_called_once_with(
tool="multiplcation_tool",
@@ -907,8 +906,121 @@ def test_crew_log_file_output(tmp_path):
)
]
test_message = {"agent": "Researcher", "task": "Say Hi"}
crew = Crew(agents=[researcher], tasks=tasks, output_log_file=str(test_file))
crew.kickoff()
assert test_file.exists()
@pytest.mark.vcr(filter_headers=["authorization"])
def test_manager_agent():
from unittest.mock import patch
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
manager = Agent(
role="Manager",
goal="Manage the crew and ensure the tasks are completed efficiently.",
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
allow_delegation=False,
)
crew = Crew(
agents=[researcher, writer],
process=Process.hierarchical,
manager_agent=manager,
tasks=[task],
)
with patch.object(Task, "execute") as execute:
crew.kickoff()
assert manager.allow_delegation is True
execute.assert_called()
def test_manager_agent_in_agents_raises_exception():
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
manager = Agent(
role="Manager",
goal="Manage the crew and ensure the tasks are completed efficiently.",
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
allow_delegation=False,
)
with pytest.raises(pydantic_core._pydantic_core.ValidationError):
Crew(
agents=[researcher, writer, manager],
process=Process.hierarchical,
manager_agent=manager,
tasks=[task],
)
def test_manager_agent_with_tools_raises_exception():
from crewai_tools import tool
@tool
def testing_tool(first_number: int, second_number: int) -> int:
"""Useful for when you need to multiply two numbers together."""
return first_number * second_number
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
manager = Agent(
role="Manager",
goal="Manage the crew and ensure the tasks are completed efficiently.",
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
allow_delegation=False,
tools=[testing_tool],
)
crew = Crew(
agents=[researcher, writer],
process=Process.hierarchical,
manager_agent=manager,
tasks=[task],
)
with pytest.raises(Exception):
crew.kickoff()
def test_crew_train_success():
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
crew = Crew(
agents=[researcher, writer],
tasks=[task],
)
crew.train(n_iterations=2)
def test_crew_train_error():
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article",
expected_output="5 bullet points with a paragraph for each idea.",
)
crew = Crew(
agents=[researcher, writer],
tasks=[task],
)
with pytest.raises(TypeError) as e:
crew.train()
assert "train() missing 1 required positional argument: 'n_iterations'" in str(
e
)

View File

@@ -0,0 +1,405 @@
interactions:
- request:
body: '{"input": ["test value test value test value test value test value test
value test value test value test value test value test value test value test
value test value test value test value test value test value"], "model": "text-embedding-ada-002",
"encoding_format": "base64"}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, br
connection:
- keep-alive
content-length:
- '292'
content-type:
- application/json
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.25.1
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.25.1
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.11.7
method: POST
uri: https://api.openai.com/v1/embeddings
response:
body:
string: !!binary |
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
GAM=
headers:
CF-Cache-Status:
- DYNAMIC
CF-RAY:
- 88142860ce7a82e2-GIG
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json
Date:
- Thu, 09 May 2024 19:39:49 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=JUH9MsoOBdCquA9K9qOgVbrLUkBpuF4d2k7EvjnBOLg-1715283589-1.0.1.1-96.yHqUmYzRiWC7xZ4.TPKu5RqvB1EFecasHfb_ix62oXcsw_Sp.gz0gDk4Inl119IxRMINM613CI5HheoJM2A;
path=/; expires=Thu, 09-May-24 20:09:49 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=DPNTnqcoTitPoe12n1vvVltuYsIzavX_dftsSEhQQcc-1715283589489-0.0.1.1-604800000;
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
Transfer-Encoding:
- chunked
access-control-allow-origin:
- '*'
alt-svc:
- h3=":443"; ma=86400
openai-model:
- text-embedding-ada-002
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '15'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=15724800; includeSubDomains
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '10000000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '9999946'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_ace5babe6674a08d07fce90792eef7eb
status:
code: 200
message: OK
- request:
body: '{"input": ["test value"], "model": "text-embedding-ada-002", "encoding_format":
"base64"}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate, br
connection:
- keep-alive
content-length:
- '89'
content-type:
- application/json
cookie:
- __cf_bm=JUH9MsoOBdCquA9K9qOgVbrLUkBpuF4d2k7EvjnBOLg-1715283589-1.0.1.1-96.yHqUmYzRiWC7xZ4.TPKu5RqvB1EFecasHfb_ix62oXcsw_Sp.gz0gDk4Inl119IxRMINM613CI5HheoJM2A;
_cfuvid=DPNTnqcoTitPoe12n1vvVltuYsIzavX_dftsSEhQQcc-1715283589489-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.25.1
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.25.1
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.11.7
method: POST
uri: https://api.openai.com/v1/embeddings
response:
body:
string: !!binary |
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
headers:
CF-Cache-Status:
- DYNAMIC
CF-RAY:
- 88142862e9f182e2-GIG
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json
Date:
- Thu, 09 May 2024 19:39:49 GMT
Server:
- cloudflare
Transfer-Encoding:
- chunked
access-control-allow-origin:
- '*'
alt-svc:
- h3=":443"; ma=86400
openai-model:
- text-embedding-ada-002
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '22'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=15724800; includeSubDomains
x-ratelimit-limit-requests:
- '10000'
x-ratelimit-limit-tokens:
- '10000000'
x-ratelimit-remaining-requests:
- '9999'
x-ratelimit-remaining-tokens:
- '9999998'
x-ratelimit-reset-requests:
- 6ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_834c84237ace9a79492cb9e8d1f68737
status:
code: 200
message: OK
version: 1

View File

@@ -20,7 +20,7 @@ def test_save_and_search(long_term_memory):
metadata={"task": "test_task", "quality": 0.5},
)
long_term_memory.save(memory)
find = long_term_memory.search("test_task")[0]
find = long_term_memory.search("test_task", latest_n=5)[0]
assert find["score"] == 0.5
assert find["datetime"] == "test_datetime"
assert find["metadata"]["agent"] == "test_agent"

View File

@@ -1,15 +1,35 @@
import pytest
from crewai.agent import Agent
from crewai.crew import Crew
from crewai.memory.short_term.short_term_memory import ShortTermMemory
from crewai.memory.short_term.short_term_memory_item import ShortTermMemoryItem
from crewai.task import Task
@pytest.fixture
def short_term_memory():
"""Fixture to create a ShortTermMemory instance"""
return ShortTermMemory()
agent = Agent(
role="Researcher",
goal="Search relevant data and provide results",
backstory="You are a researcher at a leading tech think tank.",
tools=[],
verbose=True,
)
task = Task(
description="Perform a search on specific topics.",
expected_output="A list of relevant URLs based on the search query.",
agent=agent,
)
return ShortTermMemory(crew=Crew(
agents=[agent],
tasks=[task]
))
@pytest.mark.vcr(filter_headers=["authorization"])
def test_save_and_search(short_term_memory):
memory = ShortTermMemoryItem(
data="""test value test value test value test value test value test value
@@ -19,6 +39,7 @@ def test_save_and_search(short_term_memory):
metadata={"task": "test_task"},
)
short_term_memory.save(memory)
find = short_term_memory.search("test value", score_threshold=0.01)[0]
assert find["context"] == memory.data, "Data value mismatch."
assert find["metadata"]["agent"] == "test_agent", "Agent value mismatch."

35
tests/project_test.py Normal file
View File

@@ -0,0 +1,35 @@
from crewai.agent import Agent
from crewai.project import agent, task
from crewai.task import Task
class SimpleCrew:
@agent
def simple_agent(self):
return Agent(
role="Simple Agent", goal="Simple Goal", backstory="Simple Backstory"
)
@task
def simple_task(self):
return Task(description="Simple Description", expected_output="Simple Output")
def test_agent_memoization():
crew = SimpleCrew()
first_call_result = crew.simple_agent()
second_call_result = crew.simple_agent()
assert (
first_call_result is second_call_result
), "Agent memoization is not working as expected"
def test_task_memoization():
crew = SimpleCrew()
first_call_result = crew.simple_task()
second_call_result = crew.simple_task()
assert (
first_call_result is second_call_result
), "Task memoization is not working as expected"

View File

@@ -0,0 +1,40 @@
{
"hierarchical_manager_agent": {
"role": "Lorem ipsum dolor sit amet",
"goal": "Lorem ipsum dolor sit amet",
"backstory": "Lorem ipsum dolor sit amet."
},
"planning_manager_agent": {
"role": "Lorem ipsum dolor sit amet",
"goal": "Lorem ipsum dolor sit amet",
"backstory": "Lorem ipsum dolor sit amet."
},
"slices": {
"observation": "Lorem ipsum dolor sit amet",
"task": "Lorem ipsum dolor sit amet",
"memory": "Lorem ipsum dolor sit amet",
"role_playing": "Lorem ipsum dolor sit amet",
"tools": "Lorem ipsum dolor sit amet",
"no_tools": "Lorem ipsum dolor sit amet",
"format": "Lorem ipsum dolor sit amet",
"final_answer_format": "Lorem ipsum dolor sit amet",
"format_without_tools": "Lorem ipsum dolor sit amet",
"task_with_context": "Lorem ipsum dolor sit amet",
"expected_output": "Lorem ipsum dolor sit amet",
"human_feedback": "Lorem ipsum dolor sit amet",
"getting_input": "Lorem ipsum dolor sit amet "
},
"errors": {
"force_final_answer": "Lorem ipsum dolor sit amet",
"agent_tool_unexsiting_coworker": "Lorem ipsum dolor sit amet",
"task_repeated_usage": "Lorem ipsum dolor sit amet",
"tool_usage_error": "Lorem ipsum dolor sit amet",
"tool_arguments_error": "Lorem ipsum dolor sit amet",
"wrong_tool_name": "Lorem ipsum dolor sit amet",
"tool_usage_exception": "Lorem ipsum dolor sit amet"
},
"tools": {
"delegate_work": "Lorem ipsum dolor sit amet",
"ask_question": "Lorem ipsum dolor sit amet"
}
}

View File

@@ -3,38 +3,42 @@ import pytest
from crewai.utilities.i18n import I18N
def test_load_translation():
i18n = I18N(language="en")
i18n.load_translation()
assert i18n._translations is not None
def test_load_prompts():
i18n = I18N()
i18n.load_prompts()
assert i18n._prompts is not None
def test_slice():
i18n = I18N(language="en")
i18n.load_translation()
i18n = I18N()
i18n.load_prompts()
assert isinstance(i18n.slice("role_playing"), str)
def test_errors():
i18n = I18N(language="en")
i18n.load_translation()
assert isinstance(i18n.errors("unexpected_format"), str)
def test_tools():
i18n = I18N(language="en")
i18n.load_translation()
i18n = I18N()
i18n.load_prompts()
assert isinstance(i18n.tools("ask_question"), str)
def test_retrieve():
i18n = I18N(language="en")
i18n.load_translation()
i18n = I18N()
i18n.load_prompts()
assert isinstance(i18n.retrieve("slices", "role_playing"), str)
def test_retrieve_not_found():
i18n = I18N(language="en")
i18n.load_translation()
i18n = I18N()
i18n.load_prompts()
with pytest.raises(Exception):
i18n.retrieve("nonexistent_kind", "nonexistent_key")
def test_prompt_file():
import os
path = os.path.join(os.path.dirname(__file__), "prompts.json")
i18n = I18N(prompt_file=path)
i18n.load_prompts()
assert isinstance(i18n.retrieve("slices", "role_playing"), str)
assert i18n.retrieve("slices", "role_playing") == "Lorem ipsum dolor sit amet"