mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-07 15:18:29 +00:00
* Make tests green again * Add Git validations for publishing tools (#1381) This commit prevents tools from being published if the underlying Git repository is unsynced with origin. * fix: JSON encoding date objects (#1374) * Update README (#1376) * Change all instaces of crewAI to CrewAI and fix installation step * Update the example to use YAML format * Update to come after setup and edits * Remove double tool instance * docs: correct miswritten command name (#1365) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Add `--force` option to `crewai tool publish` (#1383) This commit adds an option to bypass Git remote validations when publishing tools. * add plotting to flows documentation (#1394) * Brandon/cre 288 add telemetry to flows (#1391) * Telemetry for flows * store node names * Brandon/cre 291 flow improvements (#1390) * Implement joao feedback * update colors for crew nodes * clean up * more linting clean up * round legend corners --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> * quick fixes (#1385) * quick fixes * add generic name --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> * reduce import time by 6x (#1396) * reduce import by 6x * fix linting * Added version details (#1402) Co-authored-by: João Moura <joaomdmoura@gmail.com> * Update twitter logo to x-twiiter (#1403) * fix task cloning error (#1416) * Migrate docs from MkDocs to Mintlify (#1423) * add new mintlify docs * add favicon.svg * minor edits * add github stats * Fix/logger - fix #1412 (#1413) * improved logger * log file looks better * better lines written to log file --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> * fixing tests * preparing new version * updating init * Preparing new version * Trying to fix linting and other warnings (#1417) * Trying to fix linting * fixing more type issues * clean up ci * more ci fixes --------- Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> * Feat/poetry to uv migration (#1406) * feat: Start migrating to UV * feat: add uv to flows * feat: update docs on Poetry -> uv * feat: update docs and uv.locl * feat: update tests and github CI * feat: run ruff format * feat: update typechecking * feat: fix type checking * feat: update python version * feat: type checking gic * feat: adapt uv command to run the tool repo * Adapt tool build command to uv * feat: update logic to let only projects with crew to be deployed * feat: add uv to tools * fix; tests * fix: remove breakpoint * fix :test * feat: add crewai update to migrate from poetry to uv * fix: tests * feat: add validation for ˆ character on pyproject * feat: add run_crew to pyproject if doesnt exist * feat: add validation for poetry migration * fix: warning --------- Co-authored-by: Vinicius Brasil <vini@hey.com> * fix: training issue (#1433) * fix: training issue * fix: output from crew * fix: message * Use a slice for the manager request. Make the task use the agent i18n settings (#1446) * Fix Cache Typo in Documentation (#1441) * Correct the role for the message being added to the messages list (#1438) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * fix typo in template file (#1432) * Adapt Tools CLI to uv (#1455) * Adapt Tools CLI to UV * Fix failing test * use the same i18n as the agent for tool usage (#1440) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Upgrade docs to mirror change from `Poetry` to `UV` (#1451) * Update docs to use instead of * Add Flows YouTube tutorial & link images * feat: ADd warning from poetry -> uv (#1458) * feat/updated CLI to allow for model selection & submitting API keys (#1430) * updated CLI to allow for submitting API keys * updated click prompt to remove default number * removed all unnecessary comments * feat: implement crew creation CLI command - refactor code to multiple functions - Added ability for users to select provider and model when uing crewai create command and ave API key to .env * refactered select_choice function for early return * refactored select_provider to have an ealry return * cleanup of comments * refactor/Move functions into utils file, added new provider file and migrated fucntions thre, new constants file + general function refactor * small comment cleanup * fix unnecessary deps --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: Brandon Hancock <brandon@brandonhancock.io> * Fix incorrect parameter name in Vision tool docs page (#1461) Co-authored-by: João Moura <joaomdmoura@gmail.com> * Feat/memory base (#1444) * byom - short/entity memory * better * rm uneeded * fix text * use context * rm dep and sync * type check fix * fixed test using new cassete * fixing types * fixed types * fix types * fixed types * fixing types * fix type * cassette update * just mock the return of short term mem * remove print * try catch block * added docs * dding error handling here * preparing new version * fixing annotations * fix tasks and agents ordering * Avoiding exceptions * feat: add poetry.lock to uv migration (#1468) * fix tool calling issue (#1467) * fix tool calling issue * Update tool type check * Drop print * cutting new version * new verison * Adapt `crewai tool install <tool>` to uv (#1481) This commit updates the tool install comamnd to uv's new custom index feature. Related: https://github.com/astral-sh/uv/pull/7746/ * fix(docs): typo (#1470) * drop unneccesary tests (#1484) * drop uneccesary tests * fix linting * simplify flow (#1482) * simplify flow * propogate changes * Update docs and scripts * Template fix * make flow kickoff sync * Clean up docs * Add Cerebras LLM example configuration to LLM docs (#1488) * ensure original embedding config works (#1476) * ensure original embedding config works * some fixes * raise error on unsupported provider * WIP: brandons notes * fixes * rm prints * fixed docs * fixed run types * updates to add more docs and correct imports with huggingface embedding server enabled --------- Co-authored-by: Brandon Hancock <brandon@brandonhancock.io> * use copy to split testing and training on crews (#1491) * use copy to split testing and training on crews * make tests handle new copy functionality on train and test * fix last test * fix test * preparing new verison * fix/fixed missing API prompt + CLI docs update (#1464) * updated CLI to allow for submitting API keys * updated click prompt to remove default number * removed all unnecessary comments * feat: implement crew creation CLI command - refactor code to multiple functions - Added ability for users to select provider and model when uing crewai create command and ave API key to .env * refactered select_choice function for early return * refactored select_provider to have an ealry return * cleanup of comments * refactor/Move functions into utils file, added new provider file and migrated fucntions thre, new constants file + general function refactor * small comment cleanup * fix unnecessary deps * Added docs for new CLI provider + fixed missing API prompt * Minor doc updates * allow user to bypass api key entry + incorect number selected logic + ruff formatting * ruff updates * Fix spelling mistake --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: Brandon Hancock <brandon@brandonhancock.io> * chore(readme-fix): fixing step for 'running tests' in the contribution section (#1490) Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> * support unsafe code execution. add in docker install and running checks. (#1496) * support unsafe code execution. add in docker install and running checks. * Update return type * Fix memory imports for embedding functions (#1497) * updating crewai version * new version * new version * update plot command (#1504) * feat: add tomli so we can support 3.10 (#1506) * feat: add tomli so we can support 3.10 * feat: add validation for poetry data * Forward install command options to `uv sync` (#1510) Allow passing additional options from `crewai install` directly to `uv sync`. This enables commands like `crewai install --locked` to work as expected by forwarding all flags and options to the underlying uv command. * improve tool text description and args (#1512) * improve tool text descriptoin and args * fix lint * Drop print * add back in docstring * Improve tooling docs * Update flow docs to talk about self evaluation example * Update flow docs to talk about self evaluation example * Update flows.mdx - Fix link * Update flows cli to allow you to easily add additional crews to a flow (#1525) * Update flows cli to allow you to easily add additional crews to a flow * fix failing test * adding more error logs to test thats failing * try again * Bugfix/flows with multiple starts plus ands breaking (#1531) * bugfix/flows-with-multiple-starts-plus-ands-breaking * fix user found issue * remove prints * prepare new version * Added security.md file (#1533) * Disable telemetry explicitly (#1536) * Disable telemetry explicitly * fix linting * revert parts to og * Enhance log storage to support more data types (#1530) * Add llm providers accordion group (#1534) * add llm providers accordion group * fix numbering * Replace .netrc with uv environment variables (#1541) This commit replaces .netrc with uv environment variables for installing tools from private repositories. To store credentials, I created a new and reusable settings file for the CLI in `$HOME/.config/crewai/settings.json`. The issue with .netrc files is that they are applied system-wide and are scoped by hostname, meaning we can't differentiate tool repositories requests from regular requests to CrewAI's API. * refactor: Move BaseTool to main package and centralize tool description generation (#1514) * move base_tool to main package and consolidate tool desscription generation * update import path * update tests * update doc * add base_tool test * migrate agent delegation tools to use BaseTool * update tests * update import path for tool * fix lint * update param signature * add from_langchain to BaseTool for backwards support of langchain tools * fix the case where StructuredTool doesn't have func --------- Co-authored-by: c0dez <li@vitablehealth.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Update docs (#1550) * add llm providers accordion group * fix numbering * Fix directory tree & add llms to accordion * Feat/ibm memory (#1549) * Everything looks like its working. Waiting for lorenze review. * Update docs as well. * clean up for PR * add inputs to flows (#1553) * add inputs to flows * fix flows lint * Increase providers fetching timeout * Raise an error if an LLM doesnt return a response (#1548) * docs update (#1558) * add llm providers accordion group * fix numbering * Fix directory tree & add llms to accordion * update crewai enterprise link in docs * Feat/watson in cli (#1535) * getting cli and .env to work together for different models * support new models * clean up prints * Add support for cerebras * Fix watson keys * Fix flows to support cycles and added in test (#1556) * fix missing config (#1557) * making sure we don't check for agents that were not used in the crew * preparing new version * updating LLM docs * preparing new version * curring new version * preparing new version * preparing new version * add missing init * fix LiteLLM callback replacement * fix test_agent_usage_metrics_are_captured_for_hierarchical_process * removing prints * fix: Step callback issue (#1595) * fix: Step callback issue * fix: Add empty thought since its required * Cached prompt tokens on usage metrics * do not include cached on total * Fix crew_train_success test * feat: Reduce level for Bandit and fix code to adapt (#1604) * Add support for retrieving user preferences and memories using Mem0 (#1209) * Integrate Mem0 * Update src/crewai/memory/contextual/contextual_memory.py Co-authored-by: Deshraj Yadav <deshraj@gatech.edu> * pending commit for _fetch_user_memories * update poetry.lock * fixes mypy issues * fix mypy checks * New fixes for user_id * remove memory_provider * handle memory_provider * checks for memory_config * add mem0 to dependency * Update pyproject.toml Co-authored-by: Deshraj Yadav <deshraj@gatech.edu> * update docs * update doc * bump mem0 version * fix api error msg and mypy issue * mypy fix * resolve comments * fix memory usage without mem0 * mem0 version bump * lazy import mem0 --------- Co-authored-by: Deshraj Yadav <deshraj@gatech.edu> Co-authored-by: João Moura <joaomdmoura@gmail.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * upgrade chroma and adjust embedder function generator (#1607) * upgrade chroma and adjust embedder function generator * >= version * linted * preparing enw version * adding before and after crew * Update CLI Watson supported models + docs (#1628) * docs: add gh_token documentation to GithubSearchTool * Move kickoff callbacks to crew's domain * Cassettes * Make mypy happy * Knowledge (#1567) * initial knowledge * WIP * Adding core knowledge sources * Improve types and better support for file paths * added additional sources * fix linting * update yaml to include optional deps * adding in lorenze feedback * ensure embeddings are persisted * improvements all around Knowledge class * return this * properly reset memory * properly reset memory+knowledge * consolodation and improvements * linted * cleanup rm unused embedder * fix test * fix duplicate * generating cassettes for knowledge test * updated default embedder * None embedder to use default on pipeline cloning * improvements * fixed text_file_knowledge * mypysrc fixes * type check fixes * added extra cassette * just mocks * linted * mock knowledge query to not spin up db * linted * verbose run * put a flag * fix * adding docs * better docs * improvements from review * more docs * linted * rm print * more fixes * clearer docs * added docstrings and type hints for cli --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> Co-authored-by: Lorenze Jay <lorenzejaytech@gmail.com> * Updated README.md, fix typo(s) (#1637) * Update Perplexity example in documentation (#1623) * Fix threading * preparing new version * Log in to Tool Repository on `crewai login` (#1650) This commit adds an extra step to `crewai login` to ensure users also log in to Tool Repository, that is, exchanging their Auth0 tokens for a Tool Repository username and password to be used by UV downloads and API tool uploads. * add knowledge to mint.json * Improve typed task outputs (#1651) * V1 working * clean up imports and prints * more clean up and add tests * fixing tests * fix test * fix linting * Fix tests * Fix linting * add doc string as requested by eduardo * Update Github actions (#1639) * actions/checkout@v4 * actions/cache@v4 * actions/setup-python@v5 --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * update (#1638) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * fix spelling issue found by @Jacques-Murray (#1660) * Update readme for running mypy (#1614) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Feat/remove langchain (#1654) * feat: add initial changes from langchain * feat: remove kwargs of being processed * feat: remove langchain, update uv.lock and fix type_hint * feat: change docs * feat: remove forced requirements for parameter * feat add tests for new structure tool * feat: fix tests and adapt code for args * Feat/remove langchain (#1668) * feat: add initial changes from langchain * feat: remove kwargs of being processed * feat: remove langchain, update uv.lock and fix type_hint * feat: change docs * feat: remove forced requirements for parameter * feat add tests for new structure tool * feat: fix tests and adapt code for args * fix tool calling for langchain tools * doc strings --------- Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> * added knowledge to agent level (#1655) * added knowledge to agent level * linted * added doc * added from suggestions * added test * fixes from discussion * fix docs * fix test * rm cassette for knowledge_sources test as its a mock and update agent doc string * fix test * rm unused * linted * Update Agents docs to include two approaches for creating an agent: with and without YAML configuration * Documentation Improvements: LLM Configuration and Usage (#1684) * docs: improve tasks documentation clarity and structure - Add Task Execution Flow section - Add variable interpolation explanation - Add Task Dependencies section with examples - Improve overall document structure and readability - Update code examples with proper syntax highlighting * docs: update agent documentation with improved examples and formatting - Replace DuckDuckGoSearchRun with SerperDevTool - Update code block formatting to be consistent - Improve template examples with actual syntax - Update LLM examples to use current models - Clean up formatting and remove redundant comments * docs: enhance LLM documentation with Cerebras provider and formatting improvements * docs: simplify LLMs documentation title * docs: improve installation guide clarity and structure - Add clear Python version requirements with check command - Simplify installation options to recommended method - Improve upgrade section clarity for existing users - Add better visual structure with Notes and Tips - Update description and formatting * docs: improve introduction page organization and clarity - Update organizational analogy in Note section - Improve table formatting and alignment - Remove emojis from component table for cleaner look - Add 'helps you' to make the note more action-oriented * docs: add enterprise and community cards - Add Enterprise deployment card in quickstart - Add community card focused on open source discussions - Remove deployment reference from community description - Clean up introduction page cards - Remove link from Enterprise description text * Fixes issues with result as answer not properly exiting LLM loop (#1689) * v1 of fix implemented. Need to confirm with tokens. * remove print statements * preparing new version * fix missing code in flows docs (#1690) * docs: improve tasks documentation clarity and structure - Add Task Execution Flow section - Add variable interpolation explanation - Add Task Dependencies section with examples - Improve overall document structure and readability - Update code examples with proper syntax highlighting * docs: update agent documentation with improved examples and formatting - Replace DuckDuckGoSearchRun with SerperDevTool - Update code block formatting to be consistent - Improve template examples with actual syntax - Update LLM examples to use current models - Clean up formatting and remove redundant comments * docs: enhance LLM documentation with Cerebras provider and formatting improvements * docs: simplify LLMs documentation title * docs: improve installation guide clarity and structure - Add clear Python version requirements with check command - Simplify installation options to recommended method - Improve upgrade section clarity for existing users - Add better visual structure with Notes and Tips - Update description and formatting * docs: improve introduction page organization and clarity - Update organizational analogy in Note section - Improve table formatting and alignment - Remove emojis from component table for cleaner look - Add 'helps you' to make the note more action-oriented * docs: add enterprise and community cards - Add Enterprise deployment card in quickstart - Add community card focused on open source discussions - Remove deployment reference from community description - Clean up introduction page cards - Remove link from Enterprise description text * docs: add code snippet to Getting Started section in flows.mdx --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Update reset memories command based on the SDK (#1688) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Update using langchain tools docs (#1664) * Update example of how to use LangChain tools with correct syntax * Use .env * Add Code back --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * [FEATURE] Support for custom path in RAGStorage (#1659) * added path to RAGStorage * added path to short term and entity memory * add path for long_term_storage for completeness --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * [Doc]: Add documenation for openlit observability (#1612) * Create openlit-observability.mdx * Update doc with images and steps * Update mkdocs.yml and add OpenLIT guide link --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Fix indentation in llm-connections.mdx code block (#1573) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Knowledge project directory standard (#1691) * Knowledge project directory standard * fixed types * comment fix * made base file knowledge source an abstract class * cleaner validator on model_post_init * fix type checker * cleaner refactor * better template * Update README.md (#1694) Corrected the statement which says users can not disable telemetry, but now users can disable by setting the environment variable OTEL_SDK_DISABLED to true. Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Talk about getting structured consistent outputs with tasks. * remove all references to pipeline and pipeline router (#1661) * remove all references to pipeline and router * fix linting * drop poetry.lock * docs: add nvidia as provider (#1632) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * add knowledge demo + improve knowledge docs (#1706) * Brandon/cre 509 hitl multiple rounds of followup (#1702) * v1 of HITL working * Drop print statements * HITL code more robust. Still needs to be refactored. * refactor and more clear messages * Fix type issue * fix tests * Fix test again * Drop extra print * New docs about yaml crew with decorators. Simplify template crew with… (#1701) * New docs about yaml crew with decorators. Simplify template crew with links * Fix spelling issues. * updating tools * curting new verson * Incorporate Stale PRs that have feedback (#1693) * incorporate #1683 * add in --version flag to cli. closes #1679. * Fix env issue * Add in suggestions from @caike to make sure ragstorage doesnt exceed os file limit. Also, included additional checks to support windows. * remove poetry.lock as pointed out by @sanders41 in #1574. * Incorporate feedback from crewai reviewer * Incorporate @lorenzejay feedback * drop metadata requirement (#1712) * drop metadata requirement * fix linting * Update docs for new knowledge * more linting * more linting * make save_documents private * update docs to the new way we use knowledge and include clearing memory * add support for langfuse with litellm (#1721) * docs: Add quotes to agentops installing command (#1729) * docs: Add quotes to agentops installing command * feat: Add ContextualMemory to __init__ * feat: remove import due to circular improt * feat: update tasks config main template typos * Fixed output_file not respecting system path (#1726) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * fix:typo error (#1732) * Update crew_agent_executor.py typo error * Update en.json typo error * Fix Knowledge docs Spaceflight News API dead link * call storage.search in user context search instead of memory.search (#1692) Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> * Add doc structured tool (#1713) * Add doc structured tool * Fix example --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * _execute_tool_and_check_finality 结果给回调参数,这样就可以提前拿到结果信息,去做数据解析判断做预判 (#1716) Co-authored-by: xiaohan <fuck@qq.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * format bullet points (#1734) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Add missing @functools.wraps when wrapping functions and preserve wrapped class name in @CrewBase. (#1560) * Update annotations.py * Update utils.py * Update crew_base.py * Update utils.py * Update crew_base.py --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Fix disk I/O error when resetting short-term memory. (#1724) * Fix disk I/O error when resetting short-term memory. Reset chromadb client and nullifies references before removing directory. * Nit for clarity * did the same for knowledge_storage * cleanup * cleanup order * Cleanup after the rm of the directories --------- Co-authored-by: Lorenze Jay <lorenzejaytech@gmail.com> Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> * restrict python version compatibility (#1731) * drop 3.13 * revert * Drop test cassette that was causing error * trying to fix failing test * adding thiago changes * resolve final tests * Drop skip * Bugfix/restrict python version compatibility (#1736) * drop 3.13 * revert * Drop test cassette that was causing error * trying to fix failing test * adding thiago changes * resolve final tests * Drop skip * drop pipeline * Update pyproject.toml and uv.lock to drop crewai-tools as a default requirement (#1711) * copy googles changes. Fix tests. Improve LLM file (#1737) * copy googles changes. Fix tests. Improve LLM file * Fix type issue * fix:typo error (#1738) * Update base_agent_tools.py typo error * Update main.py typo error * Update base_file_knowledge_source.py typo error * Update test_main.py typo error * Update en.json * Update prompts.json --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Remove manager_callbacks reference (#1741) * include event emitter in flows (#1740) * include event emitter in flows * Clean up * Fix linter * sort imports with isort rules by ruff linter (#1730) * sort imports * update --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> * Added is_auto_end flag in agentops.end session in crew.py (#1320) When using agentops, we have the option to pass the `skip_auto_end_session` parameter, which is supposed to not end the session if the `end_session` function is called by Crew. Now the way it works is, the `agentops.end_session` accepts `is_auto_end` flag and crewai should have passed it as `True` (its `False` by default). I have changed the code to pass is_auto_end=True Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * NVIDIA Provider : UI changes (#1746) * docs: add nvidia as provider * nvidia ui docs changes * add note for updated list --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Fix small typo in sample tool (#1747) Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Feature/add workflow permissions (#1749) * fix: Call ChromaDB reset before removing storage directory to fix disk I/O errors * feat: add workflow permissions to stale.yml * revert rag_storage.py changes * revert rag_storage.py changes --------- Co-authored-by: Matt B <mattb@Matts-MacBook-Pro.local> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * remove pkg_resources which was causing issues (#1751) * apply agent ops changes and resolve merge conflicts (#1748) * apply agent ops changes and resolve merge conflicts * Trying to fix tests * add back in vcr * update tools * remove pkg_resources which was causing issues * Fix tests * experimenting to see if unique content is an issue with knowledge * experimenting to see if unique content is an issue with knowledge * update chromadb which seems to have issues with upsert * generate new yaml for failing test * Investigating upsert * Drop patch * Update casettes * Fix duplicate document issue * more fixes * add back in vcr * new cassette for test --------- Co-authored-by: Lorenze Jay <lorenzejaytech@gmail.com> * drop print (#1755) * Fix: CrewJSONEncoder now accepts enums (#1752) * bugfix: CrewJSONEncoder now accepts enums * sort imports --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Fix bool and null handling (#1771) * include 12 but not 13 * change to <13 instead of <=12 * Gemini 2.0 (#1773) * Update llms.mdx (Gemini 2.0) - Add Gemini 2.0 flash to Gemini table. - Add link to 2 hosting paths for Gemini in Tip. - Change to lower case model slugs vs names, user convenience. - Add https://artificialanalysis.ai/ as alternate leaderboard. - Move Gemma to "other" tab. * Update llm.py (gemini 2.0) Add setting for Gemini 2.0 context window to llm.py --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * Remove relative import in flow `main.py` template (#1782) * Add `tool.crewai.type` pyproject attribute in templates (#1789) * Correcting a small grammatical issue that was bugging me: from _satisfy the expect criteria_ to _satisfies the expected criteria_ (#1783) Signed-off-by: PJ Hagerty <pjhagerty@gmail.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> * feat: Add task guardrails feature (#1742) * feat: Add task guardrails feature Add support for custom code guardrails in tasks that validate outputs before proceeding to the next task. Features include: - Optional task-level guardrail function - Pre-next-task execution timing - Tuple return format (success, data) - Automatic result/error routing - Configurable retry mechanism - Comprehensive documentation and tests Link to Devin run: https://app.devin.ai/sessions/39f6cfd6c5a24d25a7bd70ce070ed29a Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Add type check for guardrail result and remove unused import Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Remove unnecessary f-string prefix Co-Authored-By: Joe Moura <joao@crewai.com> * feat: Add guardrail validation improvements - Add result/error exclusivity validation in GuardrailResult - Make return type annotations optional in Task guardrail validator - Improve error messages for validation failures Co-Authored-By: Joe Moura <joao@crewai.com> * docs: Add comprehensive guardrails documentation - Add type hints and examples - Add error handling best practices - Add structured error response patterns - Document retry mechanisms - Improve documentation organization Co-Authored-By: Joe Moura <joao@crewai.com> * refactor: Update guardrail functions to handle TaskOutput objects Co-Authored-By: Joe Moura <joao@crewai.com> * feat: Add task guardrails feature Add support for custom code guardrails in tasks that validate outputs before proceeding to the next task. Features include: - Optional task-level guardrail function - Pre-next-task execution timing - Tuple return format (success, data) - Automatic result/error routing - Configurable retry mechanism - Comprehensive documentation and tests Link to Devin run: https://app.devin.ai/sessions/39f6cfd6c5a24d25a7bd70ce070ed29a Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Add type check for guardrail result and remove unused import Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Remove unnecessary f-string prefix Co-Authored-By: Joe Moura <joao@crewai.com> * feat: Add guardrail validation improvements - Add result/error exclusivity validation in GuardrailResult - Make return type annotations optional in Task guardrail validator - Improve error messages for validation failures Co-Authored-By: Joe Moura <joao@crewai.com> * docs: Add comprehensive guardrails documentation - Add type hints and examples - Add error handling best practices - Add structured error response patterns - Document retry mechanisms - Improve documentation organization Co-Authored-By: Joe Moura <joao@crewai.com> * refactor: Update guardrail functions to handle TaskOutput objects Co-Authored-By: Joe Moura <joao@crewai.com> * style: Fix import sorting in task guardrails files Co-Authored-By: Joe Moura <joao@crewai.com> * fixing docs * Fixing guardarils implementation * docs: Enhance guardrail validator docstring with runtime validation rationale Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> * feat: Add interpolate_only method and improve error handling (#1791) * Fixed output_file not respecting system path * Fixed yaml config is not escaped properly for output requirements * feat: Add interpolate_only method and improve error handling - Add interpolate_only method for string interpolation while preserving JSON structure - Add comprehensive test coverage for interpolate_only - Add proper type annotation for logger using ClassVar - Improve error handling and documentation for _save_file method Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Sort imports to fix lint issues Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Reorganize imports using ruff --fix Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Consolidate imports and fix formatting Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Apply ruff automatic import sorting Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Sort imports using ruff --fix Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Frieda (Jingying) Huang <jingyingfhuang@gmail.com> Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: Frieda Huang <124417784+frieda-huang@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * Feat/docling-support (#1763) * added tool for docling support * docling support installation * use file_paths instead of file_path * fix import * organized imports * run_type docs * needs to be list * fixed logic * logged but file_path is backwards compatible * use file_paths instead of file_path 2 * added test for multiple sources for file_paths * fix run-types * enabling local files to work and type cleanup * linted * fix test and types * fixed run types * fix types * renamed to CrewDoclingSource * linted * added docs * resolve conflicts --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> Co-authored-by: Brandon Hancock <brandon@brandonhancock.io> * removed some redundancies (#1796) * removed some redundancies * cleanup * Feat/joao flow improvement requests (#1795) * Add in or and and in router * In the middle of improving plotting * final plot changes --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> * Adding Multimodal Abilities to Crew (#1805) * initial fix on delegation tools * fixing tests for delegations and coding * Refactor prepare tool and adding initial add images logic * supporting image tool * fixing linter * fix linter * Making sure multimodal feature support i18n * fix linter and types * mixxing translations * fix types and linter * Revert "fixing linter" This reverts commit ef323e3487e62ee4f5bce7f86378068a5ac77e16. * fix linters * test * fix * fix * fix linter * fix * ignore * type improvements * chore: removing crewai-tools from dev-dependencies (#1760) As mentioned in issue #1759, listing crewai-tools as dev-dependencies makes pip install it a required dependency, and not an optional Co-authored-by: João Moura <joaomdmoura@gmail.com> * docs: add guide for multimodal agents (#1807) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * Portkey Integration with CrewAI (#1233) * Create Portkey-Observability-and-Guardrails.md * crewAI update with new changes * small change --------- Co-authored-by: siddharthsambharia-portkey <siddhath.s@portkey.ai> Co-authored-by: João Moura <joaomdmoura@gmail.com> * fix: Change storage initialization to None for KnowledgeStorage (#1804) * fix: Change storage initialization to None for KnowledgeStorage * refactor: Change storage field to optional and improve error handling when saving documents --------- Co-authored-by: João Moura <joaomdmoura@gmail.com> * fix: handle optional storage with null checks (#1808) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> * docs: update README to highlight Flows (#1809) * docs: highlight Flows feature in README Co-Authored-By: Joe Moura <joao@crewai.com> * docs: enhance README with LangGraph comparison and flows-crews synergy Co-Authored-By: Joe Moura <joao@crewai.com> * docs: replace initial Flow example with advanced Flow+Crew example; enhance LangGraph comparison Co-Authored-By: Joe Moura <joao@crewai.com> * docs: incorporate key terms and enhance feature descriptions Co-Authored-By: Joe Moura <joao@crewai.com> * docs: refine technical language, enhance feature descriptions, fix string interpolation Co-Authored-By: Joe Moura <joao@crewai.com> * docs: update README with performance metrics, feature enhancements, and course links Co-Authored-By: Joe Moura <joao@crewai.com> * docs: update LangGraph comparison with paragraph and P.S. section Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * Update README.md * docs: add agent-specific knowledge documentation and examples (#1811) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * fixing file paths for knowledge source * Fix interpolation for output_file in Task (#1803) (#1814) * fix: interpolate output_file attribute from YAML Co-Authored-By: Joe Moura <joao@crewai.com> * fix: add security validation for output_file paths Co-Authored-By: Joe Moura <joao@crewai.com> * fix: add _original_output_file private attribute to fix type-checker error Co-Authored-By: Joe Moura <joao@crewai.com> * fix: update interpolate_only to handle None inputs and remove duplicate attribute Co-Authored-By: Joe Moura <joao@crewai.com> * fix: improve output_file validation and error messages Co-Authored-By: Joe Moura <joao@crewai.com> * test: add end-to-end tests for output_file functionality Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * fix(manager_llm): handle coworker role name case/whitespace properly (#1820) * fix(manager_llm): handle coworker role name case/whitespace properly - Add .strip() to agent name and role comparisons in base_agent_tools.py - Add test case for varied role name cases and whitespace - Fix issue #1503 with manager LLM delegation Co-Authored-By: Joe Moura <joao@crewai.com> * fix(manager_llm): improve error handling and add debug logging - Add debug logging for better observability - Add sanitize_agent_name helper method - Enhance error messages with more context - Add parameterized tests for edge cases: - Embedded quotes - Trailing newlines - Multiple whitespace - Case variations - None values - Improve error handling with specific exceptions Co-Authored-By: Joe Moura <joao@crewai.com> * style: fix import sorting in base_agent_tools and test_manager_llm_delegation Co-Authored-By: Joe Moura <joao@crewai.com> * fix(manager_llm): improve whitespace normalization in role name matching Co-Authored-By: Joe Moura <joao@crewai.com> * style: fix import sorting in base_agent_tools and test_manager_llm_delegation Co-Authored-By: Joe Moura <joao@crewai.com> * fix(manager_llm): add error message template for agent tool execution errors Co-Authored-By: Joe Moura <joao@crewai.com> * style: fix import sorting in test_manager_llm_delegation.py Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * fix: add tiktoken as explicit dependency and document Rust requirement (#1826) * feat: add tiktoken as explicit dependency and document Rust requirement - Add tiktoken>=0.8.0 as explicit dependency to ensure pre-built wheels are used - Document Rust compiler requirement as fallback in README.md - Addresses issue #1824 tiktoken build failure Co-Authored-By: Joe Moura <joao@crewai.com> * fix: adjust tiktoken version to ~=0.7.0 for dependency compatibility - Update tiktoken dependency to ~=0.7.0 to resolve conflict with embedchain - Maintain compatibility with crewai-tools dependency chain - Addresses CI build failures Co-Authored-By: Joe Moura <joao@crewai.com> * docs: add troubleshooting section and make tiktoken optional Co-Authored-By: Joe Moura <joao@crewai.com> * Update README.md --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> * Docstring, Error Handling, and Type Hints Improvements (#1828) * docs: add comprehensive docstrings to Flow class and methods - Added NumPy-style docstrings to all decorator functions - Added detailed documentation to Flow class methods - Included parameter types, return types, and examples - Enhanced documentation clarity and completeness Co-Authored-By: Joe Moura <joao@crewai.com> * feat: add secure path handling utilities - Add path_utils.py with safe path handling functions - Implement path validation and security checks - Integrate secure path handling in flow_visualizer.py - Add path validation in html_template_handler.py - Add comprehensive error handling for path operations Co-Authored-By: Joe Moura <joao@crewai.com> * docs: add comprehensive docstrings and type hints to flow utils (#1819) Co-Authored-By: Joe Moura <joao@crewai.com> * fix: add type annotations and fix import sorting Co-Authored-By: Joe Moura <joao@crewai.com> * fix: add type annotations to flow utils and visualization utils Co-Authored-By: Joe Moura <joao@crewai.com> * fix: resolve import sorting and type annotation issues Co-Authored-By: Joe Moura <joao@crewai.com> * fix: properly initialize and update edge_smooth variable Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> * feat: add docstring (#1819) Co-authored-by: João Moura <joaomdmoura@gmail.com> * fix: Include agent knowledge in planning process (#1818) * test: Add test demonstrating knowledge not included in planning process Issue #1703: Add test to verify that agent knowledge sources are not currently included in the planning process. This test will help validate the fix once implemented. - Creates agent with knowledge sources - Verifies knowledge context missing from planning - Checks other expected components are present Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Include agent knowledge in planning process Issue #1703: Integrate agent knowledge sources into planning summaries - Add agent_knowledge field to task summaries in planning_handler - Update test to verify knowledge inclusion - Ensure knowledge context is available during planning phase The planning agent now has access to agent knowledge when creating task execution plans, allowing for better informed planning decisions. Co-Authored-By: Joe Moura <joao@crewai.com> * style: Fix import sorting in test_knowledge_planning.py - Reorganize imports according to ruff linting rules - Fix I001 linting error Co-Authored-By: Joe Moura <joao@crewai.com> * test: Update task summary assertions to include knowledge field Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update ChromaDB mock path and fix knowledge string formatting Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Improve knowledge integration in planning process with error handling Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update task summary format for empty tools and knowledge - Change empty tools message to 'agent has no tools' - Remove agent_knowledge field when empty - Update test assertions to match new format - Improve test messages for clarity Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update string formatting for agent tools in task summary Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update string formatting for agent tools in task summary Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update string formatting for agent tools and knowledge in task summary Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update knowledge field formatting in task summary Co-Authored-By: Joe Moura <joao@crewai.com> * style: Fix import sorting in test_planning_handler.py Co-Authored-By: Joe Moura <joao@crewai.com> * style: Fix import sorting order in test_planning_handler.py Co-Authored-By: Joe Moura <joao@crewai.com> * test: Add ChromaDB mocking to test_create_tasks_summary_with_knowledge_and_tools Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> * Suppressed userWarnings from litellm pydantic issues (#1833) * Suppressed userWarnings from litellm pydantic issues * change litellm version * Fix failling ollama tasks * Trying out timeouts * Trying out timeouts * trying next crew_test timeout * trying next crew_test timeout * timeout in crew_tests * timeout in crew_tests * more timeouts * more timeouts * crew_test changes werent applied * crew_test changes werent applied * revert uv.lock * revert uv.lock * add back in crewai tool dependencies and drop litellm version * add back in crewai tool dependencies and drop litellm version * tests should work now * tests should work now * more test changes * more test changes * Reverting uv.lock and pyproject * Reverting uv.lock and pyproject * Update llama3 cassettes * Update llama3 cassettes * sync packages with uv.lock * sync packages with uv.lock * more test fixes * fix tets * drop large file * final clean up * drop record new episodes --------- Signed-off-by: PJ Hagerty <pjhagerty@gmail.com> Co-authored-by: Thiago Moretto <168731+thiagomoretto@users.noreply.github.com> Co-authored-by: Thiago Moretto <thiago.moretto@gmail.com> Co-authored-by: Vini Brasil <vini@hey.com> Co-authored-by: Guilherme de Amorim <ggimenezjr@gmail.com> Co-authored-by: Tony Kipkemboi <iamtonykipkemboi@gmail.com> Co-authored-by: Eren Küçüker <66262604+erenkucuker@users.noreply.github.com> Co-authored-by: João Moura <joaomdmoura@gmail.com> Co-authored-by: Akesh kumar <155313882+akesh-0909@users.noreply.github.com> Co-authored-by: Lennex Zinyando <brizdigital@gmail.com> Co-authored-by: Shahar Yair <shya95@gmail.com> Co-authored-by: Eduardo Chiarotti <dudumelgaco@hotmail.com> Co-authored-by: Stephen Hankinson <shankinson@gmail.com> Co-authored-by: Muhammad Noman Fareed <60171953+shnoman97@users.noreply.github.com> Co-authored-by: dbubel <50341559+dbubel@users.noreply.github.com> Co-authored-by: Rip&Tear <84775494+theCyberTech@users.noreply.github.com> Co-authored-by: Rok Benko <115651717+rokbenko@users.noreply.github.com> Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Co-authored-by: Sam <sammcj@users.noreply.github.com> Co-authored-by: Maicon Peixinho <maiconpeixinho@icloud.com> Co-authored-by: Robin Wang <6220861+MottoX@users.noreply.github.com> Co-authored-by: C0deZ <c0dezlee@gmail.com> Co-authored-by: c0dez <li@vitablehealth.com> Co-authored-by: Gui Vieira <guilherme_vieira@me.com> Co-authored-by: Dev Khant <devkhant24@gmail.com> Co-authored-by: Deshraj Yadav <deshraj@gatech.edu> Co-authored-by: Gui Vieira <gui@crewai.com> Co-authored-by: Lorenze Jay <lorenzejaytech@gmail.com> Co-authored-by: Bob Conan <sufssl03@gmail.com> Co-authored-by: Andy Bromberg <abromberg@users.noreply.github.com> Co-authored-by: Bowen Liang <bowenliang@apache.org> Co-authored-by: Ivan Peevski <133036+ipeevski@users.noreply.github.com> Co-authored-by: Rok Benko <ksjeno@gmail.com> Co-authored-by: Javier Saldaña <cjaviersaldana@outlook.com> Co-authored-by: Ola Hungerford <olahungerford@gmail.com> Co-authored-by: Tom Mahler, PhD <tom@mahler.tech> Co-authored-by: Patcher <patcher@openlit.io> Co-authored-by: Feynman Liang <feynman.liang@gmail.com> Co-authored-by: Stephen <stephen-talari@users.noreply.github.com> Co-authored-by: Rashmi Pawar <168514198+raspawar@users.noreply.github.com> Co-authored-by: Frieda Huang <124417784+frieda-huang@users.noreply.github.com> Co-authored-by: Archkon <180910180+Archkon@users.noreply.github.com> Co-authored-by: Aviral Jain <avi.aviral140@gmail.com> Co-authored-by: lgesuellip <102637283+lgesuellip@users.noreply.github.com> Co-authored-by: fuckqqcom <9391575+fuckqqcom@users.noreply.github.com> Co-authored-by: xiaohan <fuck@qq.com> Co-authored-by: Piotr Mardziel <piotrm@gmail.com> Co-authored-by: Carlos Souza <caike@users.noreply.github.com> Co-authored-by: Paul Cowgill <pauldavidcowgill@gmail.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: Anmol Deep <anmol@getaidora.com> Co-authored-by: André Lago <andrelago.eu@gmail.com> Co-authored-by: Matt B <mattb@Matts-MacBook-Pro.local> Co-authored-by: Karan Vaidya <kaavee315@gmail.com> Co-authored-by: alan blount <alan@zeroasterisk.com> Co-authored-by: PJ <pjhagerty@gmail.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: Frieda (Jingying) Huang <jingyingfhuang@gmail.com> Co-authored-by: João Igor <joaoigm@hotmail.com> Co-authored-by: siddharth Sambharia <siddharth.s@portkey.ai> Co-authored-by: siddharthsambharia-portkey <siddhath.s@portkey.ai> Co-authored-by: Erick Amorim <73451993+ericklima-ca@users.noreply.github.com> Co-authored-by: Marco Vinciguerra <88108002+VinciGit00@users.noreply.github.com>
1627 lines
49 KiB
Python
1627 lines
49 KiB
Python
"""Test Agent creation and execution basic functionality."""
|
|
|
|
import os
|
|
from unittest import mock
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from crewai import Agent, Crew, Task
|
|
from crewai.agents.cache import CacheHandler
|
|
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
|
from crewai.agents.parser import AgentAction, CrewAgentParser, OutputParserException
|
|
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
|
|
from crewai.llm import LLM
|
|
from crewai.tools import tool
|
|
from crewai.tools.tool_calling import InstructorToolCalling
|
|
from crewai.tools.tool_usage import ToolUsage
|
|
from crewai.tools.tool_usage_events import ToolUsageFinished
|
|
from crewai.utilities import RPMController
|
|
from crewai.utilities.events import Emitter
|
|
|
|
|
|
def test_agent_llm_creation_with_env_vars():
|
|
# Store original environment variables
|
|
original_api_key = os.environ.get("OPENAI_API_KEY")
|
|
original_api_base = os.environ.get("OPENAI_API_BASE")
|
|
original_model_name = os.environ.get("OPENAI_MODEL_NAME")
|
|
|
|
# Set up environment variables
|
|
os.environ["OPENAI_API_KEY"] = "test_api_key"
|
|
os.environ["OPENAI_API_BASE"] = "https://test-api-base.com"
|
|
os.environ["OPENAI_MODEL_NAME"] = "gpt-4-turbo"
|
|
|
|
# Create an agent without specifying LLM
|
|
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
|
|
|
# Check if LLM is created correctly
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "gpt-4-turbo"
|
|
assert agent.llm.api_key == "test_api_key"
|
|
assert agent.llm.base_url == "https://test-api-base.com"
|
|
|
|
# Clean up environment variables
|
|
del os.environ["OPENAI_API_KEY"]
|
|
del os.environ["OPENAI_API_BASE"]
|
|
del os.environ["OPENAI_MODEL_NAME"]
|
|
|
|
# Create an agent without specifying LLM
|
|
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
|
|
|
# Check if LLM is created correctly
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model != "gpt-4-turbo"
|
|
assert agent.llm.api_key != "test_api_key"
|
|
assert agent.llm.base_url != "https://test-api-base.com"
|
|
|
|
# Restore original environment variables
|
|
if original_api_key:
|
|
os.environ["OPENAI_API_KEY"] = original_api_key
|
|
if original_api_base:
|
|
os.environ["OPENAI_API_BASE"] = original_api_base
|
|
if original_model_name:
|
|
os.environ["OPENAI_MODEL_NAME"] = original_model_name
|
|
|
|
|
|
def test_agent_creation():
|
|
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
|
|
|
assert agent.role == "test role"
|
|
assert agent.goal == "test goal"
|
|
assert agent.backstory == "test backstory"
|
|
assert agent.tools == []
|
|
|
|
|
|
def test_agent_default_values():
|
|
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
|
assert agent.llm.model == "gpt-4o-mini"
|
|
assert agent.allow_delegation is False
|
|
|
|
|
|
def test_custom_llm():
|
|
agent = Agent(
|
|
role="test role", goal="test goal", backstory="test backstory", llm="gpt-4"
|
|
)
|
|
assert agent.llm.model == "gpt-4"
|
|
|
|
|
|
def test_custom_llm_with_langchain():
|
|
from langchain_openai import ChatOpenAI
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=ChatOpenAI(temperature=0, model="gpt-4"),
|
|
)
|
|
|
|
assert agent.llm.model == "gpt-4"
|
|
|
|
|
|
def test_custom_llm_temperature_preservation():
|
|
from langchain_openai import ChatOpenAI
|
|
|
|
langchain_llm = ChatOpenAI(temperature=0.7, model="gpt-4")
|
|
agent = Agent(
|
|
role="temperature test role",
|
|
goal="temperature test goal",
|
|
backstory="temperature test backstory",
|
|
llm=langchain_llm,
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "gpt-4"
|
|
assert agent.llm.temperature == 0.7
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task():
|
|
from langchain_openai import ChatOpenAI
|
|
|
|
from crewai import Task
|
|
|
|
agent = Agent(
|
|
role="Math Tutor",
|
|
goal="Solve math problems accurately",
|
|
backstory="You are an experienced math tutor with a knack for explaining complex concepts simply.",
|
|
llm=ChatOpenAI(temperature=0.7, model="gpt-4o-mini"),
|
|
)
|
|
|
|
task = Task(
|
|
description="Calculate the area of a circle with radius 5 cm.",
|
|
expected_output="The calculated area of the circle in square centimeters.",
|
|
agent=agent,
|
|
)
|
|
|
|
result = agent.execute_task(task)
|
|
|
|
assert result is not None
|
|
assert (
|
|
result
|
|
== "The calculated area of the circle is approximately 78.5 square centimeters."
|
|
)
|
|
assert "square centimeters" in result.lower()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execution():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="How much is 1 + 1?",
|
|
agent=agent,
|
|
expected_output="the result of the math operation.",
|
|
)
|
|
|
|
output = agent.execute_task(task)
|
|
assert output == "1 + 1 is 2"
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execution_with_tools():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[multiplier],
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="What is 3 times 4?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
with patch.object(Emitter, "emit") as emit:
|
|
output = agent.execute_task(task)
|
|
assert output == "The result of the multiplication is 12."
|
|
assert emit.call_count == 1
|
|
args, _ = emit.call_args
|
|
assert isinstance(args[1], ToolUsageFinished)
|
|
assert not args[1].from_cache
|
|
assert args[1].tool_name == "multiplier"
|
|
assert args[1].tool_args == {"first_number": 3, "second_number": 4}
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_logging_tool_usage():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[multiplier],
|
|
verbose=True,
|
|
)
|
|
|
|
assert agent.llm.model == "gpt-4o-mini"
|
|
assert agent.tools_handler.last_used_tool == {}
|
|
task = Task(
|
|
description="What is 3 times 4?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
# force cleaning cache
|
|
agent.tools_handler.cache = CacheHandler()
|
|
output = agent.execute_task(task)
|
|
tool_usage = InstructorToolCalling(
|
|
tool_name=multiplier.name, arguments={"first_number": 3, "second_number": 4}
|
|
)
|
|
|
|
assert output == "The result of the multiplication is 12."
|
|
assert agent.tools_handler.last_used_tool.tool_name == tool_usage.tool_name
|
|
assert agent.tools_handler.last_used_tool.arguments == tool_usage.arguments
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_cache_hitting():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
cache_handler = CacheHandler()
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[multiplier],
|
|
allow_delegation=False,
|
|
cache_handler=cache_handler,
|
|
verbose=True,
|
|
)
|
|
|
|
task1 = Task(
|
|
description="What is 2 times 6?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
task2 = Task(
|
|
description="What is 3 times 3?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
|
|
output = agent.execute_task(task1)
|
|
output = agent.execute_task(task2)
|
|
assert cache_handler._cache == {
|
|
"multiplier-{'first_number': 2, 'second_number': 6}": 12,
|
|
"multiplier-{'first_number': 3, 'second_number': 3}": 9,
|
|
}
|
|
|
|
task = Task(
|
|
description="What is 2 times 6 times 3? Return only the number",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
output = agent.execute_task(task)
|
|
assert output == "36"
|
|
|
|
assert cache_handler._cache == {
|
|
"multiplier-{'first_number': 2, 'second_number': 6}": 12,
|
|
"multiplier-{'first_number': 3, 'second_number': 3}": 9,
|
|
"multiplier-{'first_number': 12, 'second_number': 3}": 36,
|
|
}
|
|
|
|
with (
|
|
patch.object(CacheHandler, "read") as read,
|
|
patch.object(Emitter, "emit") as emit,
|
|
):
|
|
read.return_value = "0"
|
|
task = Task(
|
|
description="What is 2 times 6? Ignore correctness and just return the result of the multiplication tool, you must use the tool.",
|
|
agent=agent,
|
|
expected_output="The number that is the result of the multiplication tool.",
|
|
)
|
|
output = agent.execute_task(task)
|
|
assert output == "0"
|
|
read.assert_called_with(
|
|
tool="multiplier", input={"first_number": 2, "second_number": 6}
|
|
)
|
|
assert emit.call_count == 1
|
|
args, _ = emit.call_args
|
|
assert isinstance(args[1], ToolUsageFinished)
|
|
assert args[1].from_cache
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_disabling_cache_for_agent():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
cache_handler = CacheHandler()
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[multiplier],
|
|
allow_delegation=False,
|
|
cache_handler=cache_handler,
|
|
cache=False,
|
|
verbose=True,
|
|
)
|
|
|
|
task1 = Task(
|
|
description="What is 2 times 6?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
task2 = Task(
|
|
description="What is 3 times 3?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
|
|
output = agent.execute_task(task1)
|
|
output = agent.execute_task(task2)
|
|
assert cache_handler._cache != {
|
|
"multiplier-{'first_number': 2, 'second_number': 6}": 12,
|
|
"multiplier-{'first_number': 3, 'second_number': 3}": 9,
|
|
}
|
|
|
|
task = Task(
|
|
description="What is 2 times 6 times 3? Return only the number",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
output = agent.execute_task(task)
|
|
assert output == "36"
|
|
|
|
assert cache_handler._cache != {
|
|
"multiplier-{'first_number': 2, 'second_number': 6}": 12,
|
|
"multiplier-{'first_number': 3, 'second_number': 3}": 9,
|
|
"multiplier-{'first_number': 12, 'second_number': 3}": 36,
|
|
}
|
|
|
|
with patch.object(CacheHandler, "read") as read:
|
|
read.return_value = "0"
|
|
task = Task(
|
|
description="What is 2 times 6? Ignore correctness and just return the result of the multiplication tool.",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
output = agent.execute_task(task)
|
|
assert output == "12"
|
|
read.assert_not_called()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execution_with_specific_tools():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="What is 3 times 4",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
output = agent.execute_task(task=task, tools=[multiplier])
|
|
assert output == "The result of the multiplication is 12."
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_powered_by_new_o_model_family_that_allows_skipping_tool():
|
|
@tool
|
|
def multiplier(first_number: int, second_number: int) -> float:
|
|
"""Useful for when you need to multiply two numbers together."""
|
|
return first_number * second_number
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm="o1-preview",
|
|
max_iter=3,
|
|
use_system_prompt=False,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="What is 3 times 4?",
|
|
agent=agent,
|
|
expected_output="The result of the multiplication.",
|
|
)
|
|
output = agent.execute_task(task=task, tools=[multiplier])
|
|
assert output == "12"
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_powered_by_new_o_model_family_that_uses_tool():
|
|
@tool
|
|
def comapny_customer_data() -> float:
|
|
"""Useful for getting customer related data."""
|
|
return "The company has 42 customers"
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm="o1-preview",
|
|
max_iter=3,
|
|
use_system_prompt=False,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="How many customers does the company have?",
|
|
agent=agent,
|
|
expected_output="The number of customers",
|
|
)
|
|
output = agent.execute_task(task=task, tools=[comapny_customer_data])
|
|
assert output == "42"
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_custom_max_iterations():
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=1,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
with patch.object(
|
|
LLM, "call", wraps=LLM("gpt-4o", stop=["\nObservation:"]).call
|
|
) as private_mock:
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.",
|
|
expected_output="The final answer",
|
|
)
|
|
agent.execute_task(
|
|
task=task,
|
|
tools=[get_final_answer],
|
|
)
|
|
assert private_mock.call_count == 2
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_repeated_tool_usage(capsys):
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=4,
|
|
llm="gpt-4",
|
|
allow_delegation=False,
|
|
verbose=True,
|
|
)
|
|
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it until I tell you so, instead keep using the `get_final_answer` tool.",
|
|
expected_output="The final answer, don't give it until I tell you so",
|
|
)
|
|
# force cleaning cache
|
|
agent.tools_handler.cache = CacheHandler()
|
|
agent.execute_task(
|
|
task=task,
|
|
tools=[get_final_answer],
|
|
)
|
|
|
|
captured = capsys.readouterr()
|
|
|
|
assert (
|
|
"I tried reusing the same input, I must stop using this action input. I'll try something else instead."
|
|
in captured.out
|
|
)
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys):
|
|
@tool
|
|
def get_final_answer(anything: str) -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=4,
|
|
llm="gpt-4",
|
|
allow_delegation=False,
|
|
verbose=True,
|
|
cache=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it until I tell you so, instead keep using the `get_final_answer` tool.",
|
|
expected_output="The final answer, don't give it until I tell you so",
|
|
)
|
|
|
|
agent.execute_task(
|
|
task=task,
|
|
tools=[get_final_answer],
|
|
)
|
|
|
|
captured = capsys.readouterr()
|
|
assert (
|
|
"I tried reusing the same input, I must stop using this action input. I'll try something else instead."
|
|
in captured.out
|
|
)
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_moved_on_after_max_iterations():
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=3,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool over and over until you're told you can give your final answer.",
|
|
expected_output="The final answer",
|
|
)
|
|
output = agent.execute_task(
|
|
task=task,
|
|
tools=[get_final_answer],
|
|
)
|
|
assert output == "The final answer is 42."
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_respect_the_max_rpm_set(capsys):
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=5,
|
|
max_rpm=1,
|
|
verbose=True,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
|
|
moveon.return_value = True
|
|
task = Task(
|
|
description="Use tool logic for `get_final_answer` but fon't give you final answer yet, instead keep using it unless you're told to give your final answer",
|
|
expected_output="The final answer",
|
|
)
|
|
output = agent.execute_task(
|
|
task=task,
|
|
tools=[get_final_answer],
|
|
)
|
|
assert output == "The final answer is 42."
|
|
captured = capsys.readouterr()
|
|
assert "Max RPM reached, waiting for next minute to start." in captured.out
|
|
moveon.assert_called()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys):
|
|
from unittest.mock import patch
|
|
|
|
from crewai.tools import tool
|
|
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=4,
|
|
max_rpm=10,
|
|
verbose=True,
|
|
)
|
|
|
|
task = Task(
|
|
description="Use tool logic for `get_final_answer` but fon't give you final answer yet, instead keep using it unless you're told to give your final answer",
|
|
expected_output="The final answer",
|
|
tools=[get_final_answer],
|
|
agent=agent,
|
|
)
|
|
|
|
crew = Crew(agents=[agent], tasks=[task], max_rpm=1, verbose=True)
|
|
|
|
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
|
|
moveon.return_value = True
|
|
crew.kickoff()
|
|
captured = capsys.readouterr()
|
|
assert "Max RPM reached, waiting for next minute to start." not in captured.out
|
|
moveon.assert_not_called()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_without_max_rpm_respet_crew_rpm(capsys):
|
|
from unittest.mock import patch
|
|
|
|
from crewai.tools import tool
|
|
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_rpm=10,
|
|
max_iter=2,
|
|
verbose=True,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
agent2 = Agent(
|
|
role="test role2",
|
|
goal="test goal2",
|
|
backstory="test backstory2",
|
|
max_iter=1,
|
|
verbose=True,
|
|
allow_delegation=False,
|
|
)
|
|
|
|
tasks = [
|
|
Task(
|
|
description="Just say hi.", agent=agent1, expected_output="Your greeting."
|
|
),
|
|
Task(
|
|
description="NEVER give a Final Answer, unless you are told otherwise, instead keep using the `get_final_answer` tool non-stop, until you must give you best final answer",
|
|
expected_output="The final answer",
|
|
tools=[get_final_answer],
|
|
agent=agent2,
|
|
),
|
|
]
|
|
|
|
crew = Crew(agents=[agent1, agent2], tasks=tasks, max_rpm=1, verbose=True)
|
|
|
|
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
|
|
moveon.return_value = True
|
|
crew.kickoff()
|
|
captured = capsys.readouterr()
|
|
assert "get_final_answer" in captured.out
|
|
assert "Max RPM reached, waiting for next minute to start." in captured.out
|
|
moveon.assert_called_once()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_error_on_parsing_tool(capsys):
|
|
from unittest.mock import patch
|
|
|
|
from crewai.tools import tool
|
|
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=1,
|
|
verbose=True,
|
|
)
|
|
tasks = [
|
|
Task(
|
|
description="Use the get_final_answer tool.",
|
|
expected_output="The final answer",
|
|
agent=agent1,
|
|
tools=[get_final_answer],
|
|
)
|
|
]
|
|
|
|
crew = Crew(
|
|
agents=[agent1],
|
|
tasks=tasks,
|
|
verbose=True,
|
|
function_calling_llm="gpt-4o",
|
|
)
|
|
with patch.object(ToolUsage, "_original_tool_calling") as force_exception_1:
|
|
force_exception_1.side_effect = Exception("Error on parsing tool.")
|
|
with patch.object(ToolUsage, "_render") as force_exception_2:
|
|
force_exception_2.side_effect = Exception("Error on parsing tool.")
|
|
crew.kickoff()
|
|
captured = capsys.readouterr()
|
|
assert "Error on parsing tool." in captured.out
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_remembers_output_format_after_using_tools_too_many_times():
|
|
from unittest.mock import patch
|
|
|
|
from crewai.tools import tool
|
|
|
|
@tool
|
|
def get_final_answer() -> float:
|
|
"""Get the final answer but don't give it yet, just re-use this
|
|
tool non-stop."""
|
|
return 42
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_iter=6,
|
|
verbose=True,
|
|
)
|
|
tasks = [
|
|
Task(
|
|
description="Use tool logic for `get_final_answer` but fon't give you final answer yet, instead keep using it unless you're told to give your final answer",
|
|
expected_output="The final answer",
|
|
agent=agent1,
|
|
tools=[get_final_answer],
|
|
)
|
|
]
|
|
|
|
crew = Crew(agents=[agent1], tasks=tasks, verbose=True)
|
|
|
|
with patch.object(ToolUsage, "_remember_format") as remember_format:
|
|
crew.kickoff()
|
|
remember_format.assert_called()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_use_specific_tasks_output_as_context(capsys):
|
|
agent1 = Agent(role="test role", goal="test goal", backstory="test backstory")
|
|
agent2 = Agent(role="test role2", goal="test goal2", backstory="test backstory2")
|
|
|
|
say_hi_task = Task(
|
|
description="Just say hi.", agent=agent1, expected_output="Your greeting."
|
|
)
|
|
say_bye_task = Task(
|
|
description="Just say bye.", agent=agent1, expected_output="Your farewell."
|
|
)
|
|
answer_task = Task(
|
|
description="Answer accordingly to the context you got.",
|
|
expected_output="Your answer.",
|
|
context=[say_hi_task],
|
|
agent=agent2,
|
|
)
|
|
|
|
tasks = [say_hi_task, say_bye_task, answer_task]
|
|
|
|
crew = Crew(agents=[agent1, agent2], tasks=tasks)
|
|
result = crew.kickoff()
|
|
|
|
assert "bye" not in result.raw.lower()
|
|
assert "hi" in result.raw.lower() or "hello" in result.raw.lower()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_step_callback():
|
|
class StepCallback:
|
|
def callback(self, step):
|
|
pass
|
|
|
|
with patch.object(StepCallback, "callback") as callback:
|
|
|
|
@tool
|
|
def learn_about_AI() -> str:
|
|
"""Useful for when you need to learn about AI to write an paragraph about it."""
|
|
return "AI is a very broad field."
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[learn_about_AI],
|
|
step_callback=StepCallback().callback,
|
|
)
|
|
|
|
essay = Task(
|
|
description="Write and then review an small paragraph on AI until it's AMAZING",
|
|
expected_output="The final paragraph.",
|
|
agent=agent1,
|
|
)
|
|
tasks = [essay]
|
|
crew = Crew(agents=[agent1], tasks=tasks)
|
|
|
|
callback.return_value = "ok"
|
|
crew.kickoff()
|
|
callback.assert_called()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_function_calling_llm():
|
|
llm = "gpt-4o"
|
|
|
|
@tool
|
|
def learn_about_AI() -> str:
|
|
"""Useful for when you need to learn about AI to write an paragraph about it."""
|
|
return "AI is a very broad field."
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
tools=[learn_about_AI],
|
|
llm="gpt-4o",
|
|
max_iter=2,
|
|
function_calling_llm=llm,
|
|
)
|
|
|
|
essay = Task(
|
|
description="Write and then review an small paragraph on AI until it's AMAZING",
|
|
expected_output="The final paragraph.",
|
|
agent=agent1,
|
|
)
|
|
tasks = [essay]
|
|
crew = Crew(agents=[agent1], tasks=tasks)
|
|
from unittest.mock import patch
|
|
|
|
import instructor
|
|
|
|
from crewai.tools.tool_usage import ToolUsage
|
|
|
|
with (
|
|
patch.object(
|
|
instructor, "from_litellm", wraps=instructor.from_litellm
|
|
) as mock_from_litellm,
|
|
patch.object(
|
|
ToolUsage,
|
|
"_original_tool_calling",
|
|
side_effect=Exception("Forced exception"),
|
|
) as mock_original_tool_calling,
|
|
):
|
|
crew.kickoff()
|
|
mock_from_litellm.assert_called()
|
|
mock_original_tool_calling.assert_called()
|
|
|
|
|
|
def test_agent_count_formatting_error():
|
|
from unittest.mock import patch
|
|
|
|
agent1 = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
verbose=True,
|
|
)
|
|
|
|
parser = CrewAgentParser(agent=agent1)
|
|
|
|
with patch.object(Agent, "increment_formatting_errors") as mock_count_errors:
|
|
test_text = "This text does not match expected formats."
|
|
with pytest.raises(OutputParserException):
|
|
parser.parse(test_text)
|
|
mock_count_errors.assert_called_once()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_tool_result_as_answer_is_the_final_answer_for_the_agent():
|
|
from crewai.tools import BaseTool
|
|
|
|
class MyCustomTool(BaseTool):
|
|
name: str = "Get Greetings"
|
|
description: str = "Get a random greeting back"
|
|
|
|
def _run(self) -> str:
|
|
return "Howdy!"
|
|
|
|
agent1 = Agent(
|
|
role="Data Scientist",
|
|
goal="Product amazing resports on AI",
|
|
backstory="You work with data and AI",
|
|
tools=[MyCustomTool(result_as_answer=True)],
|
|
)
|
|
|
|
essay = Task(
|
|
description="Write and then review an small paragraph on AI until it's AMAZING. But first use the `Get Greetings` tool to get a greeting.",
|
|
expected_output="The final paragraph with the full review on AI and no greeting.",
|
|
agent=agent1,
|
|
)
|
|
tasks = [essay]
|
|
crew = Crew(agents=[agent1], tasks=tasks)
|
|
|
|
result = crew.kickoff()
|
|
assert result.raw == "Howdy!"
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_tool_usage_information_is_appended_to_agent():
|
|
from crewai.tools import BaseTool
|
|
|
|
class MyCustomTool(BaseTool):
|
|
name: str = "Decide Greetings"
|
|
description: str = "Decide what is the appropriate greeting to use"
|
|
|
|
def _run(self) -> str:
|
|
return "Howdy!"
|
|
|
|
agent1 = Agent(
|
|
role="Friendly Neighbor",
|
|
goal="Make everyone feel welcome",
|
|
backstory="You are the friendly neighbor",
|
|
tools=[MyCustomTool(result_as_answer=True)],
|
|
)
|
|
|
|
greeting = Task(
|
|
description="Say an appropriate greeting.",
|
|
expected_output="The greeting.",
|
|
agent=agent1,
|
|
)
|
|
tasks = [greeting]
|
|
crew = Crew(agents=[agent1], tasks=tasks)
|
|
|
|
crew.kickoff()
|
|
assert agent1.tools_results == [
|
|
{
|
|
"result": "Howdy!",
|
|
"tool_name": "Decide Greetings",
|
|
"tool_args": {},
|
|
"result_as_answer": True,
|
|
}
|
|
]
|
|
|
|
|
|
def test_agent_definition_based_on_dict():
|
|
config = {
|
|
"role": "test role",
|
|
"goal": "test goal",
|
|
"backstory": "test backstory",
|
|
"verbose": True,
|
|
}
|
|
|
|
agent = Agent(**config)
|
|
|
|
assert agent.role == "test role"
|
|
assert agent.goal == "test goal"
|
|
assert agent.backstory == "test backstory"
|
|
assert agent.verbose is True
|
|
assert agent.tools == []
|
|
|
|
|
|
# test for human input
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_human_input():
|
|
# Agent configuration
|
|
config = {
|
|
"role": "test role",
|
|
"goal": "test goal",
|
|
"backstory": "test backstory",
|
|
}
|
|
|
|
agent = Agent(**config)
|
|
|
|
# Task configuration with human input enabled
|
|
task = Task(
|
|
agent=agent,
|
|
description="Say the word: Hi",
|
|
expected_output="The word: Hi",
|
|
human_input=True,
|
|
)
|
|
|
|
# Side effect function for _ask_human_input to simulate multiple feedback iterations
|
|
feedback_responses = iter(
|
|
[
|
|
"Don't say hi, say Hello instead!", # First feedback
|
|
"looks good", # Second feedback to exit loop
|
|
]
|
|
)
|
|
|
|
def ask_human_input_side_effect(*args, **kwargs):
|
|
return next(feedback_responses)
|
|
|
|
with patch.object(
|
|
CrewAgentExecutor, "_ask_human_input", side_effect=ask_human_input_side_effect
|
|
) as mock_human_input:
|
|
# Execute the task
|
|
output = agent.execute_task(task)
|
|
|
|
# Assertions to ensure the agent behaves correctly
|
|
assert mock_human_input.call_count == 2 # Should have asked for feedback twice
|
|
assert output.strip().lower() == "hello" # Final output should be 'Hello'
|
|
|
|
|
|
def test_interpolate_inputs():
|
|
agent = Agent(
|
|
role="{topic} specialist",
|
|
goal="Figure {goal} out",
|
|
backstory="I am the master of {role}",
|
|
)
|
|
|
|
agent.interpolate_inputs({"topic": "AI", "goal": "life", "role": "all things"})
|
|
assert agent.role == "AI specialist"
|
|
assert agent.goal == "Figure life out"
|
|
assert agent.backstory == "I am the master of all things"
|
|
|
|
agent.interpolate_inputs({"topic": "Sales", "goal": "stuff", "role": "nothing"})
|
|
assert agent.role == "Sales specialist"
|
|
assert agent.goal == "Figure stuff out"
|
|
assert agent.backstory == "I am the master of nothing"
|
|
|
|
|
|
def test_not_using_system_prompt():
|
|
agent = Agent(
|
|
role="{topic} specialist",
|
|
goal="Figure {goal} out",
|
|
backstory="I am the master of {role}",
|
|
use_system_prompt=False,
|
|
)
|
|
|
|
agent.create_agent_executor()
|
|
assert not agent.agent_executor.prompt.get("user")
|
|
assert not agent.agent_executor.prompt.get("system")
|
|
|
|
|
|
def test_using_system_prompt():
|
|
agent = Agent(
|
|
role="{topic} specialist",
|
|
goal="Figure {goal} out",
|
|
backstory="I am the master of {role}",
|
|
)
|
|
|
|
agent.create_agent_executor()
|
|
assert agent.agent_executor.prompt.get("user")
|
|
assert agent.agent_executor.prompt.get("system")
|
|
|
|
|
|
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|>""",
|
|
)
|
|
|
|
expected_prompt = """<|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:<|eot_id|>
|
|
<|start_header_id|>assistant<|end_header_id|>
|
|
|
|
"""
|
|
|
|
with patch.object(CrewAgentExecutor, "_format_prompt") as mock_format_prompt:
|
|
mock_format_prompt.return_value = expected_prompt
|
|
|
|
# Trigger the _format_prompt method
|
|
agent.agent_executor._format_prompt("dummy_prompt", {})
|
|
|
|
# Assert that _format_prompt was called
|
|
mock_format_prompt.assert_called_once()
|
|
|
|
# Assert that the returned prompt matches the expected prompt
|
|
assert mock_format_prompt.return_value == expected_prompt
|
|
|
|
|
|
@patch("crewai.agent.CrewTrainingHandler")
|
|
def test_agent_training_handler(crew_training_handler):
|
|
task_prompt = "What is 1 + 1?"
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
verbose=True,
|
|
)
|
|
crew_training_handler().load.return_value = {
|
|
f"{str(agent.id)}": {"0": {"human_feedback": "good"}}
|
|
}
|
|
|
|
result = agent._training_handler(task_prompt=task_prompt)
|
|
|
|
assert result == "What is 1 + 1?\n\nYou MUST follow these instructions: \n good"
|
|
|
|
crew_training_handler.assert_has_calls(
|
|
[mock.call(), mock.call("training_data.pkl"), mock.call().load()]
|
|
)
|
|
|
|
|
|
@patch("crewai.agent.CrewTrainingHandler")
|
|
def test_agent_use_trained_data(crew_training_handler):
|
|
task_prompt = "What is 1 + 1?"
|
|
agent = Agent(
|
|
role="researcher",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
verbose=True,
|
|
)
|
|
crew_training_handler().load.return_value = {
|
|
agent.role: {
|
|
"suggestions": [
|
|
"The result of the math operation must be right.",
|
|
"Result must be better than 1.",
|
|
]
|
|
}
|
|
}
|
|
|
|
result = agent._use_trained_data(task_prompt=task_prompt)
|
|
|
|
assert (
|
|
result == "What is 1 + 1?\n\nYou MUST follow these instructions: \n"
|
|
" - The result of the math operation must be right.\n - Result must be better than 1."
|
|
)
|
|
crew_training_handler.assert_has_calls(
|
|
[mock.call(), mock.call("trained_agents_data.pkl"), mock.call().load()]
|
|
)
|
|
|
|
|
|
def test_agent_max_retry_limit():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
max_retry_limit=1,
|
|
)
|
|
|
|
task = Task(
|
|
agent=agent,
|
|
description="Say the word: Hi",
|
|
expected_output="The word: Hi",
|
|
human_input=True,
|
|
)
|
|
|
|
error_message = "Error happening while sending prompt to model."
|
|
with patch.object(
|
|
CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke
|
|
) as invoke_mock:
|
|
invoke_mock.side_effect = Exception(error_message)
|
|
|
|
assert agent._times_executed == 0
|
|
assert agent.max_retry_limit == 1
|
|
|
|
with pytest.raises(Exception) as e:
|
|
agent.execute_task(
|
|
task=task,
|
|
)
|
|
assert e.value.args[0] == error_message
|
|
assert agent._times_executed == 2
|
|
|
|
invoke_mock.assert_has_calls(
|
|
[
|
|
mock.call(
|
|
{
|
|
"input": "Say the word: Hi\n\nThis is the expect criteria for your final answer: The word: Hi\nyou MUST return the actual complete content as the final answer, not a summary.",
|
|
"tool_names": "",
|
|
"tools": "",
|
|
"ask_for_human_input": True,
|
|
}
|
|
),
|
|
mock.call(
|
|
{
|
|
"input": "Say the word: Hi\n\nThis is the expect criteria for your final answer: The word: Hi\nyou MUST return the actual complete content as the final answer, not a summary.",
|
|
"tool_names": "",
|
|
"tools": "",
|
|
"ask_for_human_input": True,
|
|
}
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def test_agent_with_llm():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo", temperature=0.7),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "gpt-3.5-turbo"
|
|
assert agent.llm.temperature == 0.7
|
|
|
|
|
|
def test_agent_with_custom_stop_words():
|
|
stop_words = ["STOP", "END"]
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo", stop=stop_words),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert set(agent.llm.stop) == set(stop_words + ["\nObservation:"])
|
|
assert all(word in agent.llm.stop for word in stop_words)
|
|
assert "\nObservation:" in agent.llm.stop
|
|
|
|
|
|
def test_agent_with_callbacks():
|
|
def dummy_callback(response):
|
|
pass
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo", callbacks=[dummy_callback]),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert len(agent.llm.callbacks) == 1
|
|
assert agent.llm.callbacks[0] == dummy_callback
|
|
|
|
|
|
def test_agent_with_additional_kwargs():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(
|
|
model="gpt-3.5-turbo",
|
|
temperature=0.8,
|
|
top_p=0.9,
|
|
presence_penalty=0.1,
|
|
frequency_penalty=0.1,
|
|
),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "gpt-3.5-turbo"
|
|
assert agent.llm.temperature == 0.8
|
|
assert agent.llm.top_p == 0.9
|
|
assert agent.llm.presence_penalty == 0.1
|
|
assert agent.llm.frequency_penalty == 0.1
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_llm_call():
|
|
llm = LLM(model="gpt-3.5-turbo")
|
|
messages = [{"role": "user", "content": "Say 'Hello, World!'"}]
|
|
|
|
response = llm.call(messages)
|
|
assert "Hello, World!" in response
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_llm_call_with_error():
|
|
llm = LLM(model="non-existent-model")
|
|
messages = [{"role": "user", "content": "This should fail"}]
|
|
|
|
with pytest.raises(Exception):
|
|
llm.call(messages)
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_handle_context_length_exceeds_limit():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
)
|
|
original_action = AgentAction(
|
|
tool="test_tool",
|
|
tool_input="test_input",
|
|
text="test_log",
|
|
thought="test_thought",
|
|
)
|
|
|
|
with patch.object(
|
|
CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke
|
|
) as private_mock:
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.",
|
|
expected_output="The final answer",
|
|
)
|
|
agent.execute_task(
|
|
task=task,
|
|
)
|
|
private_mock.assert_called_once()
|
|
with patch.object(
|
|
CrewAgentExecutor, "_handle_context_length"
|
|
) as mock_handle_context:
|
|
mock_handle_context.side_effect = ValueError(
|
|
"Context length limit exceeded"
|
|
)
|
|
|
|
long_input = "This is a very long input. " * 10000
|
|
|
|
# Attempt to handle context length, expecting the mocked error
|
|
with pytest.raises(ValueError) as excinfo:
|
|
agent.agent_executor._handle_context_length(
|
|
[(original_action, long_input)]
|
|
)
|
|
|
|
assert "Context length limit exceeded" in str(excinfo.value)
|
|
mock_handle_context.assert_called_once()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_handle_context_length_exceeds_limit_cli_no():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
sliding_context_window=False,
|
|
)
|
|
task = Task(description="test task", agent=agent, expected_output="test output")
|
|
|
|
with patch.object(
|
|
CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke
|
|
) as private_mock:
|
|
task = Task(
|
|
description="The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.",
|
|
expected_output="The final answer",
|
|
)
|
|
agent.execute_task(
|
|
task=task,
|
|
)
|
|
private_mock.assert_called_once()
|
|
pytest.raises(SystemExit)
|
|
with patch.object(
|
|
CrewAgentExecutor, "_handle_context_length"
|
|
) as mock_handle_context:
|
|
mock_handle_context.assert_not_called()
|
|
|
|
|
|
def test_agent_with_all_llm_attributes():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(
|
|
model="gpt-3.5-turbo",
|
|
timeout=10,
|
|
temperature=0.7,
|
|
top_p=0.9,
|
|
n=1,
|
|
stop=["STOP", "END"],
|
|
max_tokens=100,
|
|
presence_penalty=0.1,
|
|
frequency_penalty=0.1,
|
|
logit_bias={50256: -100}, # Example: bias against the EOT token
|
|
response_format={"type": "json_object"},
|
|
seed=42,
|
|
logprobs=True,
|
|
top_logprobs=5,
|
|
base_url="https://api.openai.com/v1",
|
|
api_version="2023-05-15",
|
|
api_key="sk-your-api-key-here",
|
|
),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "gpt-3.5-turbo"
|
|
assert agent.llm.timeout == 10
|
|
assert agent.llm.temperature == 0.7
|
|
assert agent.llm.top_p == 0.9
|
|
assert agent.llm.n == 1
|
|
assert set(agent.llm.stop) == set(["STOP", "END", "\nObservation:"])
|
|
assert all(word in agent.llm.stop for word in ["STOP", "END", "\nObservation:"])
|
|
assert agent.llm.max_tokens == 100
|
|
assert agent.llm.presence_penalty == 0.1
|
|
assert agent.llm.frequency_penalty == 0.1
|
|
assert agent.llm.logit_bias == {50256: -100}
|
|
assert agent.llm.response_format == {"type": "json_object"}
|
|
assert agent.llm.seed == 42
|
|
assert agent.llm.logprobs
|
|
assert agent.llm.top_logprobs == 5
|
|
assert agent.llm.base_url == "https://api.openai.com/v1"
|
|
assert agent.llm.api_version == "2023-05-15"
|
|
assert agent.llm.api_key == "sk-your-api-key-here"
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_llm_call_with_all_attributes():
|
|
llm = LLM(
|
|
model="gpt-3.5-turbo",
|
|
temperature=0.7,
|
|
max_tokens=50,
|
|
stop=["STOP"],
|
|
presence_penalty=0.1,
|
|
frequency_penalty=0.1,
|
|
)
|
|
messages = [{"role": "user", "content": "Say 'Hello, World!' and then say STOP"}]
|
|
|
|
response = llm.call(messages)
|
|
assert "Hello, World!" in response
|
|
assert "STOP" not in response
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_with_ollama_llama3():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="ollama/llama3.2:3b", base_url="http://localhost:11434"),
|
|
)
|
|
|
|
assert isinstance(agent.llm, LLM)
|
|
assert agent.llm.model == "ollama/llama3.2:3b"
|
|
assert agent.llm.base_url == "http://localhost:11434"
|
|
|
|
task = "Respond in 20 words. Which model are you?"
|
|
response = agent.llm.call([{"role": "user", "content": task}])
|
|
|
|
assert response
|
|
assert len(response.split()) <= 25 # Allow a little flexibility in word count
|
|
assert "Llama3" in response or "AI" in response or "language model" in response
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_llm_call_with_ollama_llama3():
|
|
llm = LLM(
|
|
model="ollama/llama3.2:3b",
|
|
base_url="http://localhost:11434",
|
|
temperature=0.7,
|
|
max_tokens=30,
|
|
)
|
|
messages = [
|
|
{"role": "user", "content": "Respond in 20 words. Which model are you?"}
|
|
]
|
|
|
|
response = llm.call(messages)
|
|
|
|
assert response
|
|
assert len(response.split()) <= 25 # Allow a little flexibility in word count
|
|
assert "Llama3" in response or "AI" in response or "language model" in response
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task_basic():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo"),
|
|
)
|
|
|
|
task = Task(
|
|
description="Calculate 2 + 2",
|
|
expected_output="The result of the calculation",
|
|
agent=agent,
|
|
)
|
|
|
|
result = agent.execute_task(task)
|
|
assert "4" in result
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task_with_context():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo"),
|
|
)
|
|
|
|
task = Task(
|
|
description="Summarize the given context in one sentence",
|
|
expected_output="A one-sentence summary",
|
|
agent=agent,
|
|
)
|
|
|
|
context = "The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet."
|
|
|
|
result = agent.execute_task(task, context=context)
|
|
assert len(result.split(".")) == 3
|
|
assert "fox" in result.lower() and "dog" in result.lower()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task_with_tool():
|
|
@tool
|
|
def dummy_tool(query: str) -> str:
|
|
"""Useful for when you need to get a dummy result for a query."""
|
|
return f"Dummy result for: {query}"
|
|
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo"),
|
|
tools=[dummy_tool],
|
|
)
|
|
|
|
task = Task(
|
|
description="Use the dummy tool to get a result for 'test query'",
|
|
expected_output="The result from the dummy tool",
|
|
agent=agent,
|
|
)
|
|
|
|
result = agent.execute_task(task)
|
|
assert "Dummy result for: test query" in result
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task_with_custom_llm():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="gpt-3.5-turbo", temperature=0.7, max_tokens=50),
|
|
)
|
|
|
|
task = Task(
|
|
description="Write a haiku about AI",
|
|
expected_output="A haiku (3 lines, 5-7-5 syllable pattern) about AI",
|
|
agent=agent,
|
|
)
|
|
|
|
result = agent.execute_task(task)
|
|
assert result.startswith(
|
|
"Artificial minds,\nCoding thoughts in circuits bright,\nAI's silent might."
|
|
)
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_execute_task_with_ollama():
|
|
agent = Agent(
|
|
role="test role",
|
|
goal="test goal",
|
|
backstory="test backstory",
|
|
llm=LLM(model="ollama/llama3.2:3b", base_url="http://localhost:11434"),
|
|
)
|
|
|
|
task = Task(
|
|
description="Explain what AI is in one sentence",
|
|
expected_output="A one-sentence explanation of AI",
|
|
agent=agent,
|
|
)
|
|
|
|
result = agent.execute_task(task)
|
|
assert len(result.split(".")) == 2
|
|
assert "AI" in result or "artificial intelligence" in result.lower()
|
|
|
|
|
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
|
def test_agent_with_knowledge_sources():
|
|
# Create a knowledge source with some content
|
|
content = "Brandon's favorite color is red and he likes Mexican food."
|
|
string_source = StringKnowledgeSource(content=content)
|
|
|
|
with patch(
|
|
"crewai.knowledge.storage.knowledge_storage.KnowledgeStorage"
|
|
) as MockKnowledge:
|
|
mock_knowledge_instance = MockKnowledge.return_value
|
|
mock_knowledge_instance.sources = [string_source]
|
|
mock_knowledge_instance.query.return_value = [{"content": content}]
|
|
|
|
agent = Agent(
|
|
role="Information Agent",
|
|
goal="Provide information based on knowledge sources",
|
|
backstory="You have access to specific knowledge sources.",
|
|
llm=LLM(model="gpt-4o-mini"),
|
|
knowledge_sources=[string_source],
|
|
)
|
|
|
|
# Create a task that requires the agent to use the knowledge
|
|
task = Task(
|
|
description="What is Brandon's favorite color?",
|
|
expected_output="Brandon's favorite color.",
|
|
agent=agent,
|
|
)
|
|
|
|
crew = Crew(agents=[agent], tasks=[task])
|
|
result = crew.kickoff()
|
|
|
|
# Assert that the agent provides the correct information
|
|
assert "red" in result.raw.lower()
|