From 3e003f5e3209e0ce815b0a0214c56fd6c830e861 Mon Sep 17 00:00:00 2001 From: Gui Vieira Date: Wed, 20 Nov 2024 10:06:49 -0300 Subject: [PATCH] Move kickoff callbacks to crew's domain --- src/crewai/crew.py | 22 +- src/crewai/project/__init__.py | 8 +- src/crewai/project/annotations.py | 25 +- src/crewai/project/crew_base.py | 33 +- .../cassettes/test_crew_execution_order.yaml | 450 ------------------ tests/project_test.py | 109 ++--- 6 files changed, 82 insertions(+), 565 deletions(-) delete mode 100644 tests/cassettes/test_crew_execution_order.yaml diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 04820adf8..fb03acb5b 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -5,7 +5,7 @@ import uuid import warnings from concurrent.futures import Future from hashlib import md5 -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union from pydantic import ( UUID4, @@ -36,9 +36,7 @@ from crewai.telemetry import Telemetry from crewai.tools.agent_tools.agent_tools import AgentTools from crewai.types.usage_metrics import UsageMetrics from crewai.utilities import I18N, FileHandler, Logger, RPMController -from crewai.utilities.constants import ( - TRAINING_DATA_FILE, -) +from crewai.utilities.constants import TRAINING_DATA_FILE from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator from crewai.utilities.evaluators.task_evaluator import TaskEvaluator from crewai.utilities.formatter import ( @@ -165,6 +163,16 @@ class Crew(BaseModel): default=None, description="Callback to be executed after each task for all agents execution.", ) + before_kickoff_callbacks: List[ + Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]] + ] = Field( + default_factory=list, + description="List of callbacks to be executed before crew kickoff. It may be used to adjust inputs before the crew is executed.", + ) + after_kickoff_callbacks: List[Callable[[CrewOutput], CrewOutput]] = Field( + default_factory=list, + description="List of callbacks to be executed after crew kickoff. It may be used to adjust the output of the crew.", + ) max_rpm: Optional[int] = Field( default=None, description="Maximum number of requests per minute for the crew execution to be respected.", @@ -478,6 +486,9 @@ class Crew(BaseModel): self, inputs: Optional[Dict[str, Any]] = None, ) -> CrewOutput: + for callback in self.before_kickoff_callbacks: + inputs = callback(inputs) + """Starts the crew to work on its assigned tasks.""" self._execution_span = self._telemetry.crew_execution_span(self, inputs) self._task_output_handler.reset() @@ -520,6 +531,9 @@ class Crew(BaseModel): f"The process '{self.process}' is not implemented yet." ) + for callback in self.after_kickoff_callbacks: + result = callback(result) + metrics += [agent._token_process.get_summary() for agent in self.agents] self.usage_metrics = UsageMetrics() diff --git a/src/crewai/project/__init__.py b/src/crewai/project/__init__.py index 1dbb6e41b..25f6f5e04 100644 --- a/src/crewai/project/__init__.py +++ b/src/crewai/project/__init__.py @@ -1,5 +1,7 @@ from .annotations import ( + after_kickoff, agent, + before_kickoff, cache_handler, callback, crew, @@ -9,8 +11,6 @@ from .annotations import ( pipeline, task, tool, - before_crew, - after_crew, ) from .crew_base import CrewBase from .pipeline_base import PipelineBase @@ -28,6 +28,6 @@ __all__ = [ "llm", "cache_handler", "pipeline", - "before_crew", - "after_crew", + "before_kickoff", + "after_kickoff", ] diff --git a/src/crewai/project/annotations.py b/src/crewai/project/annotations.py index c1a497fa1..83e396a4c 100644 --- a/src/crewai/project/annotations.py +++ b/src/crewai/project/annotations.py @@ -5,13 +5,13 @@ from crewai import Crew from crewai.project.utils import memoize -def before_crew(func): - func.is_before_crew = True +def before_kickoff(func): + func.is_before_kickoff = True return func -def after_crew(func): - func.is_after_crew = True +def after_kickoff(func): + func.is_after_kickoff = True return func @@ -109,6 +109,19 @@ def crew(func) -> Callable[..., Crew]: self.agents = instantiated_agents self.tasks = instantiated_tasks - return func(self, *args, **kwargs) + crew = func(self, *args, **kwargs) - return wrapper + def callback_wrapper(callback, instance): + def wrapper(*args, **kwargs): + return callback(instance, *args, **kwargs) + + return wrapper + + for _, callback in self._before_kickoff.items(): + crew.before_kickoff_callbacks.append(callback_wrapper(callback, self)) + for _, callback in self._after_kickoff.items(): + crew.after_kickoff_callbacks.append(callback_wrapper(callback, self)) + + return crew + + return memoize(wrapper) diff --git a/src/crewai/project/crew_base.py b/src/crewai/project/crew_base.py index ed48b24d1..e93452a6e 100644 --- a/src/crewai/project/crew_base.py +++ b/src/crewai/project/crew_base.py @@ -43,8 +43,8 @@ def CrewBase(cls: T) -> T: for attr in [ "is_task", "is_agent", - "is_before_crew", - "is_after_crew", + "is_before_kickoff", + "is_after_kickoff", "is_kickoff", ] ) @@ -57,11 +57,11 @@ def CrewBase(cls: T) -> T: self._original_agents = self._filter_functions( self._original_functions, "is_agent" ) - self._before_crew = self._filter_functions( - self._original_functions, "is_before_crew" + self._before_kickoff = self._filter_functions( + self._original_functions, "is_before_kickoff" ) - self._after_crew = self._filter_functions( - self._original_functions, "is_after_crew" + self._after_kickoff = self._filter_functions( + self._original_functions, "is_after_kickoff" ) self._kickoff = self._filter_functions( self._original_functions, "is_kickoff" @@ -213,25 +213,4 @@ def CrewBase(cls: T) -> T: callback_functions[callback]() for callback in callbacks ] - def kickoff(self, inputs=None): - # Execute before_crew functions and allow them to modify inputs - for _, func in self._before_crew.items(): - modified_inputs = func(self, inputs) - if modified_inputs is not None: - inputs = modified_inputs - - # Get the crew instance - crew_instance = self.crew() - - # Execute the crew's tasks - result = crew_instance.kickoff(inputs=inputs) - - # Execute after_crew functions and allow them to modify the output - for _, func in self._after_crew.items(): - modified_result = func(self, result) - if modified_result is not None: - result = modified_result - - return result - return cast(T, WrappedClass) diff --git a/tests/cassettes/test_crew_execution_order.yaml b/tests/cassettes/test_crew_execution_order.yaml deleted file mode 100644 index 5f05a3258..000000000 --- a/tests/cassettes/test_crew_execution_order.yaml +++ /dev/null @@ -1,450 +0,0 @@ -interactions: -- request: - body: !!binary | - CuMOCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSug4KEgoQY3Jld2FpLnRl - bGVtZXRyeRKSDAoQiPNFpMW9CoeJ9Zm3+M0txRIIWLnalHui3g4qDENyZXcgQ3JlYXRlZDABOcAS - H7rirwgYQRBNIbrirwgYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODAuMEoaCg5weXRob25fdmVy - c2lvbhIICgYzLjExLjdKLgoIY3Jld19rZXkSIgogZjM0NmE5YWQ2ZDczMDYzZTA2NzdiMTdjZTlj - NTAxNzdKMQoHY3Jld19pZBImCiQ1MTZhMTZiNi1mMWY2LTRlMzUtYjY3Ni05ZjRiMGY3MTBhZDRK - HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf - bnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSqwFCgtjcmV3 - X2FnZW50cxKcBQqZBVt7ImtleSI6ICI3M2MzNDljOTNjMTYzYjVkNGRmOThhNjRmYWMxYzQzMCIs - ICJpZCI6ICI1N2ZhM2QwNC04ZTQ5LTQyMDMtOTg2OS1lNzliMDdiYWJkNGMiLCAicm9sZSI6ICJ7 - dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4 - X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwg - ImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29k - ZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMi - OiBbXX0sIHsia2V5IjogImJiMDY4Mzc3YzE2NDFiZTZkN2Q5N2E1MTY1OWRiNjEzIiwgImlkIjog - ImM5NzA4YTVjLTI4MjEtNDI4Ny05ZDkyLWE3MmU1NWEyMTZlYiIsICJyb2xlIjogInt0b3BpY30g - UmVwb3J0aW5nIEFuYWx5c3RcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwg - Im1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQt - NG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/ - IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSpMECgpj - cmV3X3Rhc2tzEoQECoEEW3sia2V5IjogIjZhZmM0YjM5NjI1OWZiYjc2ODFmNTZjNzc1NWNjOTM3 - IiwgImlkIjogIjYxOWYxNWFhLTc1NDItNDJiOC04MDZhLWJlNmVhZTQwYmYyMyIsICJhc3luY19l - eGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAi - e3RvcGljfSBTZW5pb3IgRGF0YSBSZXNlYXJjaGVyXG4iLCAiYWdlbnRfa2V5IjogIjczYzM0OWM5 - M2MxNjNiNWQ0ZGY5OGE2NGZhYzFjNDMwIiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICJi - MTdiMTg4ZGJmMTRmOTNhOThlNWI5NWFhZDM2NzU3NyIsICJpZCI6ICJhNGIzOGJjYi1jN2Y3LTRk - MTMtYjFiNC00Yzc0ZmE4NzIxOTQiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5f - aW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogInt0b3BpY30gUmVwb3J0aW5nIEFuYWx5c3Rc - biIsICJhZ2VudF9rZXkiOiAiYmIwNjgzNzdjMTY0MWJlNmQ3ZDk3YTUxNjU5ZGI2MTMiLCAidG9v - bHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQaSYAFNgUUCxMT0z+KwJCrBIIrjh6pRvY2zEq - DFRhc2sgQ3JlYXRlZDABOag/LbrirwgYQTilLbrirwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFk - NmQ3MzA2M2UwNjc3YjE3Y2U5YzUwMTc3SjEKB2NyZXdfaWQSJgokNTE2YTE2YjYtZjFmNi00ZTM1 - LWI2NzYtOWY0YjBmNzEwYWQ0Si4KCHRhc2tfa2V5EiIKIDZhZmM0YjM5NjI1OWZiYjc2ODFmNTZj - Nzc1NWNjOTM3SjEKB3Rhc2tfaWQSJgokNjE5ZjE1YWEtNzU0Mi00MmI4LTgwNmEtYmU2ZWFlNDBi - ZjIzegIYAYUBAAEAAA== - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '1894' - Content-Type: - - application/x-protobuf - User-Agent: - - OTel-OTLP-Exporter-Python/1.27.0 - method: POST - uri: https://telemetry.crewai.com:4319/v1/traces - response: - body: - string: "\n\0" - headers: - Content-Length: - - '2' - Content-Type: - - application/x-protobuf - Date: - - Sun, 17 Nov 2024 07:10:46 GMT - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"role": "system", "content": "You are LLMs Senior Data Researcher\n. - You''re a seasoned researcher with a knack for uncovering the latest developments - in LLMs. Known for your ability to find the most relevant information and present - it in a clear and concise manner.\n\nYour personal goal is: Uncover cutting-edge - developments in LLMs\n\nTo give my best complete final answer to the task use - the exact following format:\n\nThought: I now can give a great answer\nFinal - Answer: Your final answer must be the great and the most complete as possible, - it must be outcome described.\n\nI MUST use these formats, my job depends on - it!"}, {"role": "user", "content": "\nCurrent Task: Conduct a thorough research - about LLMs Make sure you find any interesting and relevant information given - the current year is 2024.\n\n\nThis is the expect criteria for your final answer: - A list with 10 bullet points of the most relevant information about LLMs\n\nyou - MUST return the actual complete content as the final answer, not a summary.\n\nBegin! - This is VERY important to you, use the tools available and give your best Final - Answer, your job depends on it!\n\nThought:"}], "model": "gpt-4o", "stop": ["\nObservation:"], - "stream": false}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '1235' - content-type: - - application/json - cookie: - - __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ; - _cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000 - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.52.1 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.52.1 - x-stainless-raw-response: - - 'true' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.11.7 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - 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 - headers: - CF-Cache-Status: - - DYNAMIC - CF-RAY: - - 8e3de71c79986217-GRU - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Sun, 17 Nov 2024 07:10:50 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '5446' - openai-version: - - '2020-10-01' - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-ratelimit-limit-requests: - - '10000' - x-ratelimit-limit-tokens: - - '30000000' - x-ratelimit-remaining-requests: - - '9999' - x-ratelimit-remaining-tokens: - - '29999713' - x-ratelimit-reset-requests: - - 6ms - x-ratelimit-reset-tokens: - - 0s - x-request-id: - - req_dda88f1d73ab714daf68612f9936d111 - status: - code: 200 - message: OK -- request: - body: !!binary | - Cs4CCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSpQIKEgoQY3Jld2FpLnRl - bGVtZXRyeRKOAgoQyRM1X58vc+Q8vTI/ofl1GBIIY3Nm20DB7JsqDFRhc2sgQ3JlYXRlZDABOZgm - aAvkrwgYQehgagvkrwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFkNmQ3MzA2M2UwNjc3YjE3Y2U5 - YzUwMTc3SjEKB2NyZXdfaWQSJgokNTE2YTE2YjYtZjFmNi00ZTM1LWI2NzYtOWY0YjBmNzEwYWQ0 - Si4KCHRhc2tfa2V5EiIKIGIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3SjEKB3Rhc2tf - aWQSJgokYTRiMzhiY2ItYzdmNy00ZDEzLWIxYjQtNGM3NGZhODcyMTk0egIYAYUBAAEAAA== - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '337' - Content-Type: - - application/x-protobuf - User-Agent: - - OTel-OTLP-Exporter-Python/1.27.0 - method: POST - uri: https://telemetry.crewai.com:4319/v1/traces - response: - body: - string: "\n\0" - headers: - Content-Length: - - '2' - Content-Type: - - application/x-protobuf - Date: - - Sun, 17 Nov 2024 07:10:51 GMT - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"role": "system", "content": "You are LLMs Reporting Analyst\n. - You''re a meticulous analyst with a keen eye for detail. You''re known for your - ability to turn complex data into clear and concise reports, making it easy - for others to understand and act on the information you provide.\nYour personal - goal is: Create detailed reports based on LLMs data analysis and research findings\n\nTo - give my best complete final answer to the task use the exact following format:\n\nThought: - I now can give a great answer\nFinal Answer: Your final answer must be the great - and the most complete as possible, it must be outcome described.\n\nI MUST use - these formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent - Task: Review the context you got and expand each topic into a full section for - a report. Make sure the report is detailed and contains any and all relevant - information.\n\n\nThis is the expect criteria for your final answer: A fully - fledge reports with the mains topics, each with a full section of information. - Formatted as markdown without ''```''\n\nyou MUST return the actual complete - content as the final answer, not a summary.\n\nThis is the context you''re working - with:\n1. **Advancements in Architectural Design**: In 2024, LLMs have seen - significant improvements in architecture, with models adopting more layered - and complex structures that enhance contextual understanding and prevent degradation - in performance over long text sequences. This has improved accuracy and coherence - in generating human-like text.\n\n2. **Integration with Multimodal Data**: The - latest LLMs have integrated multimodal capabilities, allowing them to process - and generate not just text, but also integrate and interpret images, audio, - and video data. This integration significantly enhances user interaction capabilities - and widens practical applications, particularly in fields such as creative industries - and customer service.\n\n3. **Efficiency and Scalability**: There is a strong - focus on reducing the carbon footprint of training and deploying LLMs. Techniques - such as model distillation and the use of more efficient training processes - are commonplace. Models are also being designed for scalability to better serve - a wide range of applications without requiring extensive computational resources.\n\n4. - **Fine-Tuning and Customization**: The ability to finely tune LLMs for specific - industry or organizational needs has become more advanced. Developers can now - more easily customize these models to align with particular styles, industry - jargon, or brands, enhancing relevance and utility across diverse applications.\n\n5. - **Ethical AI and Safety Measures**: A growing emphasis is placed on ethical - considerations. Frameworks for ensuring fairness, reducing biases, and enhancing - transparency in outputs are being developed. New protocols are in place to predict - and mitigate potential misuses of LLMs to improve trust and reliability.\n\n6. - **Proliferation of Open Source Models**: With the community demanding transparency - and flexibility, many open-source LLMs have been developed to promote research - and innovation. These models provide robust alternatives to commercial counterparts - and enhance collaborative development efforts across the globe.\n\n7. **Emergence - of LLM-Driven Applications**: 2024 has seen a rise in applications based on - LLM technology, including advanced customer service bots, dynamic content creation - tools, and educational software that personalizes learning experiences. This - expands the role of LLMs beyond traditional academic or entertainment purposes.\n\n8. - **Focus on Low-Resource Language Support**: LLMs are becoming more adept at - understanding and generating languages with fewer resources, vastly improving - global accessibility and allowing technology to support a wider array of linguistic - communities. This helps bridge digital divides and promote inclusivity.\n\n9. - **Enhanced Interaction Techniques**: Novel interaction paradigms, such as real-time - adaptations and on-the-fly learning, enable LLMs to adjust tone, complexity, - and content more responsively to user feedback. This leads to more natural, - engaging, and personalized interactions in various interface environments.\n\n10. - **Collaborative Intelligence**: There''s an emerging trend of using LLMs in - collaborative settings where AI assists human decision-making by synthesizing - large datasets to suggest insights and trends. This symbiosis enhances productivity - and innovation in research and business contexts.\n\nBegin! This is VERY important - to you, use the tools available and give your best Final Answer, your job depends - on it!\n\nThought:"}], "model": "gpt-4o", "stop": ["\nObservation:"], "stream": - false}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '4742' - content-type: - - application/json - cookie: - - __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ; - _cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000 - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.52.1 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.52.1 - x-stainless-raw-response: - - 'true' - x-stainless-retry-count: - - '0' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.11.7 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - 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 - L4f7OwUCdjevN/8BAAD//wMA9xdqlskdAAA= - headers: - CF-Cache-Status: - - DYNAMIC - CF-RAY: - - 8e3de74049116217-GRU - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Sun, 17 Nov 2024 07:11:00 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '9414' - openai-version: - - '2020-10-01' - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-ratelimit-limit-requests: - - '10000' - x-ratelimit-limit-tokens: - - '30000000' - x-ratelimit-remaining-requests: - - '9999' - x-ratelimit-remaining-tokens: - - '29998843' - x-ratelimit-reset-requests: - - 6ms - x-ratelimit-reset-tokens: - - 2ms - x-request-id: - - req_ffd793f9d60c093888d2483ea4305dd6 - status: - code: 200 - message: OK -version: 1 diff --git a/tests/project_test.py b/tests/project_test.py index 329886a25..6c68f4993 100644 --- a/tests/project_test.py +++ b/tests/project_test.py @@ -1,10 +1,10 @@ -from crewai.agent import Agent -from crewai.project import agent, task, before_crew, after_crew, crew -from crewai.project import CrewBase -from crewai.task import Task -from crewai.crew import Crew import pytest +from crewai.agent import Agent +from crewai.crew import Crew +from crewai.project import CrewBase, after_kickoff, agent, before_kickoff, crew, task +from crewai.task import Task + class SimpleCrew: @agent @@ -47,13 +47,13 @@ class TestCrew: def reporting_task(self): return Task(config=self.tasks_config["reporting_task"]) - @before_crew + @before_kickoff def modify_inputs(self, inputs): if inputs: inputs["topic"] = "Bicycles" return inputs - @after_crew + @after_kickoff def modify_outputs(self, outputs): outputs.raw = outputs.raw + " post processed" return outputs @@ -83,6 +83,16 @@ def test_task_memoization(): ), "Task memoization is not working as expected" +def test_crew_memoization(): + crew = TestCrew() + first_call_result = crew.crew() + second_call_result = crew.crew() + + assert ( + first_call_result is second_call_result + ), "Crew references should point to the same object" + + def test_task_name(): simple_task = SimpleCrew().simple_task() assert ( @@ -96,32 +106,33 @@ def test_task_name(): @pytest.mark.vcr(filter_headers=["authorization"]) -def test_before_crew_modification(): +def test_before_kickoff_modification(): crew = TestCrew() inputs = {"topic": "LLMs"} - result = crew.kickoff(inputs=inputs) - print(result.raw) - assert "bicycles" in result.raw, "Before crew function did not modify inputs" + result = crew.crew().kickoff(inputs=inputs) + assert "bicycles" in result.raw, "Before kickoff function did not modify inputs" @pytest.mark.vcr(filter_headers=["authorization"]) -def test_after_crew_modification(): +def test_after_kickoff_modification(): crew = TestCrew() # Assuming the crew execution returns a dict - result = crew.kickoff({"topic": "LLMs"}) + result = crew.crew().kickoff({"topic": "LLMs"}) - assert "post processed" in result.raw, "After crew function did not modify outputs" + assert ( + "post processed" in result.raw + ), "After kickoff function did not modify outputs" @pytest.mark.vcr(filter_headers=["authorization"]) -def test_before_crew_with_none_input(): +def test_before_kickoff_with_none_input(): crew = TestCrew() crew.crew().kickoff(None) # Test should pass without raising exceptions @pytest.mark.vcr(filter_headers=["authorization"]) -def test_multiple_before_after_crew(): +def test_multiple_before_after_kickoff(): @CrewBase class MultipleHooksCrew: agents_config = "config/agents.yaml" @@ -143,22 +154,22 @@ def test_multiple_before_after_crew(): def reporting_task(self): return Task(config=self.tasks_config["reporting_task"]) - @before_crew + @before_kickoff def first_before(self, inputs): inputs["topic"] = "Bicycles" return inputs - @before_crew + @before_kickoff def second_before(self, inputs): inputs["topic"] = "plants" return inputs - @after_crew + @after_kickoff def first_after(self, outputs): outputs.raw = outputs.raw + " processed first" return outputs - @after_crew + @after_kickoff def second_after(self, outputs): outputs.raw = outputs.raw + " processed second" return outputs @@ -168,58 +179,8 @@ def test_multiple_before_after_crew(): return Crew(agents=self.agents, tasks=self.tasks, verbose=True) crew = MultipleHooksCrew() - result = crew.kickoff({"topic": "LLMs"}) + result = crew.crew().kickoff({"topic": "LLMs"}) - assert "plants" in result.raw, "First before_crew not executed" - assert "processed first" in result.raw, "First after_crew not executed" - assert "processed second" in result.raw, "Second after_crew not executed" - - -@pytest.mark.vcr(filter_headers=["authorization"]) -def test_crew_execution_order(): - execution_order = [] - - @CrewBase - class OrderTestCrew: - agents_config = "config/agents.yaml" - tasks_config = "config/tasks.yaml" - - @agent - def researcher(self): - return Agent(config=self.agents_config["researcher"]) - - @agent - def reporting_analyst(self): - return Agent(config=self.agents_config["reporting_analyst"]) - - @task - def research_task(self): - execution_order.append("task") - return Task(config=self.tasks_config["research_task"]) - - @task - def reporting_task(self): - return Task(config=self.tasks_config["reporting_task"]) - - @before_crew - def before(self, inputs): - execution_order.append("before") - return inputs - - @after_crew - def after(self, outputs): - execution_order.append("after") - return outputs - - @crew - def crew(self): - return Crew(agents=self.agents, tasks=self.tasks, verbose=True) - - crew = OrderTestCrew() - crew.kickoff({"topic": "LLMs"}) - - assert execution_order == [ - "before", - "task", - "after", - ], "Crew execution order is incorrect" + assert "plants" in result.raw, "First before_kickoff not executed" + assert "processed first" in result.raw, "First after_kickoff not executed" + assert "processed second" in result.raw, "Second after_kickoff not executed"