Merge branch 'main' into feat/remove-langchain

This commit is contained in:
Eduardo Chiarotti
2024-11-26 16:56:41 -03:00
committed by GitHub
10 changed files with 122 additions and 31 deletions

View File

@@ -6,7 +6,7 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Install Requirements - name: Install Requirements
run: | run: |

View File

@@ -13,10 +13,10 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.10' python-version: '3.10'
@@ -25,7 +25,7 @@ jobs:
run: echo "::set-output name=hash::$(sha256sum requirements-doc.txt | awk '{print $1}')" run: echo "::set-output name=hash::$(sha256sum requirements-doc.txt | awk '{print $1}')"
- name: Setup cache - name: Setup cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
key: mkdocs-material-${{ steps.req-hash.outputs.hash }} key: mkdocs-material-${{ steps.req-hash.outputs.hash }}
path: .cache path: .cache
@@ -42,4 +42,4 @@ jobs:
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
- name: Build and deploy MkDocs - name: Build and deploy MkDocs
run: mkdocs gh-deploy --force run: mkdocs gh-deploy --force

View File

@@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: "3.11.9" python-version: "3.11.9"

View File

@@ -14,7 +14,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: "3.11.9" python-version: "3.11.9"

View File

@@ -121,7 +121,7 @@ researcher:
You're a seasoned researcher with a knack for uncovering the latest You're a seasoned researcher with a knack for uncovering the latest
developments in {topic}. Known for your ability to find the most relevant developments in {topic}. Known for your ability to find the most relevant
information and present it in a clear and concise manner. information and present it in a clear and concise manner.
reporting_analyst: reporting_analyst:
role: > role: >
{topic} Reporting Analyst {topic} Reporting Analyst
@@ -205,7 +205,7 @@ class LatestAiDevelopmentCrew():
tasks=self.tasks, # Automatically created by the @task decorator tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential, process=Process.sequential,
verbose=True, verbose=True,
) )
``` ```
**main.py** **main.py**
@@ -357,7 +357,7 @@ uv run pytest .
### Running static type checks ### Running static type checks
```bash ```bash
uvx mypy uvx mypy src
``` ```
### Packaging ### Packaging

View File

@@ -11,10 +11,12 @@ from crewai.agents.crew_agent_executor import CrewAgentExecutor
from crewai.cli.constants import ENV_VARS from crewai.cli.constants import ENV_VARS
from crewai.llm import LLM from crewai.llm import LLM
from crewai.memory.contextual.contextual_memory import ContextualMemory from crewai.memory.contextual.contextual_memory import ContextualMemory
from crewai.task import Task
from crewai.tools import BaseTool from crewai.tools import BaseTool
from crewai.tools.agent_tools.agent_tools import AgentTools from crewai.tools.agent_tools.agent_tools import AgentTools
from crewai.utilities import Converter, Prompts from crewai.utilities import Converter, Prompts
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
from crewai.utilities.converter import generate_model_description
from crewai.utilities.token_counter_callback import TokenCalcHandler from crewai.utilities.token_counter_callback import TokenCalcHandler
from crewai.utilities.training_handler import CrewTrainingHandler from crewai.utilities.training_handler import CrewTrainingHandler
@@ -124,7 +126,7 @@ class Agent(BaseAgent):
@model_validator(mode="after") @model_validator(mode="after")
def post_init_setup(self): def post_init_setup(self):
self.agent_ops_agent_name = self.role self.agent_ops_agent_name = self.role
unnacepted_attributes = [ unaccepted_attributes = [
"AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY",
"AWS_REGION_NAME", "AWS_REGION_NAME",
@@ -158,28 +160,23 @@ class Agent(BaseAgent):
for provider, env_vars in ENV_VARS.items(): for provider, env_vars in ENV_VARS.items():
if provider == set_provider: if provider == set_provider:
for env_var in env_vars: for env_var in env_vars:
if env_var["key_name"] in unnacepted_attributes:
continue
# Check if the environment variable is set # Check if the environment variable is set
if "key_name" in env_var: key_name = env_var.get("key_name")
env_value = os.environ.get(env_var["key_name"]) if key_name and key_name not in unaccepted_attributes:
env_value = os.environ.get(key_name)
if env_value: if env_value:
# Map key names containing "API_KEY" to "api_key" # Map key names containing "API_KEY" to "api_key"
key_name = ( key_name = (
"api_key" "api_key" if "API_KEY" in key_name else key_name
if "API_KEY" in env_var["key_name"]
else env_var["key_name"]
) )
# Map key names containing "API_BASE" to "api_base" # Map key names containing "API_BASE" to "api_base"
key_name = ( key_name = (
"api_base" "api_base" if "API_BASE" in key_name else key_name
if "API_BASE" in env_var["key_name"]
else key_name
) )
# Map key names containing "API_VERSION" to "api_version" # Map key names containing "API_VERSION" to "api_version"
key_name = ( key_name = (
"api_version" "api_version"
if "API_VERSION" in env_var["key_name"] if "API_VERSION" in key_name
else key_name else key_name
) )
llm_params[key_name] = env_value llm_params[key_name] = env_value
@@ -237,7 +234,7 @@ class Agent(BaseAgent):
def execute_task( def execute_task(
self, self,
task: Any, task: Task,
context: Optional[str] = None, context: Optional[str] = None,
tools: Optional[List[BaseTool]] = None, tools: Optional[List[BaseTool]] = None,
) -> str: ) -> str:
@@ -256,6 +253,22 @@ class Agent(BaseAgent):
task_prompt = task.prompt() task_prompt = task.prompt()
# If the task requires output in JSON or Pydantic format,
# append specific instructions to the task prompt to ensure
# that the final answer does not include any code block markers
if task.output_json or task.output_pydantic:
# Generate the schema based on the output format
if task.output_json:
# schema = json.dumps(task.output_json, indent=2)
schema = generate_model_description(task.output_json)
elif task.output_pydantic:
schema = generate_model_description(task.output_pydantic)
task_prompt += "\n" + self.i18n.slice("formatted_task_instructions").format(
output_format=schema
)
if context: if context:
task_prompt = self.i18n.slice("task_with_context").format( task_prompt = self.i18n.slice("task_with_context").format(
task=task_prompt, context=context task=task_prompt, context=context

View File

@@ -279,9 +279,7 @@ class Task(BaseModel):
content = ( content = (
json_output json_output
if json_output if json_output
else pydantic_output.model_dump_json() else pydantic_output.model_dump_json() if pydantic_output else result
if pydantic_output
else result
) )
self._save_file(content) self._save_file(content)

View File

@@ -11,7 +11,7 @@
"role_playing": "You are {role}. {backstory}\nYour personal goal is: {goal}", "role_playing": "You are {role}. {backstory}\nYour personal goal is: {goal}",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n", "tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"no_tools": "\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!", "no_tools": "\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!",
"format": "I MUST either use a tool (use one at time) OR give my best final answer not both at the same time. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\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\n ", "format": "I MUST either use a tool (use one at time) OR give my best final answer not both at the same time. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\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\n",
"final_answer_format": "If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\n\n", "final_answer_format": "If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\n\n",
"format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\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\n", "format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\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\n",
"task_with_context": "{task}\n\nThis is the context you're working with:\n{context}", "task_with_context": "{task}\n\nThis is the context you're working with:\n{context}",
@@ -21,7 +21,8 @@
"summarizer_system_message": "You are a helpful assistant that summarizes text.", "summarizer_system_message": "You are a helpful assistant that summarizes text.",
"sumamrize_instruction": "Summarize the following text, make sure to include all the important information: {group}", "sumamrize_instruction": "Summarize the following text, make sure to include all the important information: {group}",
"summary": "This is a summary of our conversation so far:\n{merged_summary}", "summary": "This is a summary of our conversation so far:\n{merged_summary}",
"manager_request": "Your best answer to your coworker asking you this, accounting for the context shared." "manager_request": "Your best answer to your coworker asking you this, accounting for the context shared.",
"formatted_task_instructions": "Ensure your final answer contains only the content in the following format: {output_format}\n\nEnsure the final output does not include any code block markers like ```json or ```python."
}, },
"errors": { "errors": {
"force_final_answer_error": "You can't keep going, this was the best you could do.\n {formatted_answer.text}", "force_final_answer_error": "You can't keep going, this was the best you could do.\n {formatted_answer.text}",

View File

@@ -1,6 +1,6 @@
import json import json
import re import re
from typing import Any, Optional, Type, Union from typing import Any, Optional, Type, Union, get_args, get_origin
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
@@ -214,3 +214,38 @@ def create_converter(
raise Exception("No output converter found or set.") raise Exception("No output converter found or set.")
return converter return converter
def generate_model_description(model: Type[BaseModel]) -> str:
"""
Generate a string description of a Pydantic model's fields and their types.
This function takes a Pydantic model class and returns a string that describes
the model's fields and their respective types. The description includes handling
of complex types such as `Optional`, `List`, and `Dict`, as well as nested Pydantic
models.
"""
def describe_field(field_type):
origin = get_origin(field_type)
args = get_args(field_type)
if origin is Union and type(None) in args:
non_none_args = [arg for arg in args if arg is not type(None)]
return f"Optional[{describe_field(non_none_args[0])}]"
elif origin is list:
return f"List[{describe_field(args[0])}]"
elif origin is dict:
key_type = describe_field(args[0])
value_type = describe_field(args[1])
return f"Dict[{key_type}, {value_type}]"
elif isinstance(field_type, type) and issubclass(field_type, BaseModel):
return generate_model_description(field_type)
else:
return field_type.__name__
fields = model.__annotations__
field_descriptions = [
f'"{name}": {describe_field(type_)}' for name, type_ in fields.items()
]
return "{\n " + ",\n ".join(field_descriptions) + "\n}"

View File

@@ -1,7 +1,10 @@
import json import json
from typing import Dict, List, Optional
from unittest.mock import MagicMock, Mock, patch from unittest.mock import MagicMock, Mock, patch
import pytest import pytest
from pydantic import BaseModel
from crewai.llm import LLM from crewai.llm import LLM
from crewai.utilities.converter import ( from crewai.utilities.converter import (
Converter, Converter,
@@ -9,12 +12,11 @@ from crewai.utilities.converter import (
convert_to_model, convert_to_model,
convert_with_instructions, convert_with_instructions,
create_converter, create_converter,
generate_model_description,
get_conversion_instructions, get_conversion_instructions,
handle_partial_json, handle_partial_json,
validate_model, validate_model,
) )
from pydantic import BaseModel
from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser
@@ -269,3 +271,45 @@ def test_create_converter_fails_without_agent_or_converter_cls():
create_converter( create_converter(
llm=Mock(), text="Sample", model=SimpleModel, instructions="Convert" llm=Mock(), text="Sample", model=SimpleModel, instructions="Convert"
) )
def test_generate_model_description_simple_model():
description = generate_model_description(SimpleModel)
expected_description = '{\n "name": str,\n "age": int\n}'
assert description == expected_description
def test_generate_model_description_nested_model():
description = generate_model_description(NestedModel)
expected_description = (
'{\n "id": int,\n "data": {\n "name": str,\n "age": int\n}\n}'
)
assert description == expected_description
def test_generate_model_description_optional_field():
class ModelWithOptionalField(BaseModel):
name: Optional[str]
age: int
description = generate_model_description(ModelWithOptionalField)
expected_description = '{\n "name": Optional[str],\n "age": int\n}'
assert description == expected_description
def test_generate_model_description_list_field():
class ModelWithListField(BaseModel):
items: List[int]
description = generate_model_description(ModelWithListField)
expected_description = '{\n "items": List[int]\n}'
assert description == expected_description
def test_generate_model_description_dict_field():
class ModelWithDictField(BaseModel):
attributes: Dict[str, int]
description = generate_model_description(ModelWithDictField)
expected_description = '{\n "attributes": Dict[str, int]\n}'
assert description == expected_description