Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back AgentOps as Optional Dependency #543

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f6ee12d
implements agentops with a langchain handler, agent tracking and tool…
bboynton97 Mar 20, 2024
f67d0a2
track tool usage
bboynton97 Mar 21, 2024
a9339fc
end session after completion
bboynton97 Mar 26, 2024
c44c2b6
track tool usage time
bboynton97 Mar 29, 2024
5e46514
better tool and llm tracking
bboynton97 Mar 30, 2024
4402c9b
merge upstream
bboynton97 Apr 2, 2024
45e307b
code cleanup
bboynton97 Apr 2, 2024
67bc1de
make agentops optional
bboynton97 Apr 3, 2024
215e398
optional dependency usage
bboynton97 Apr 4, 2024
7500854
remove telemetry code
bboynton97 Apr 4, 2024
79a0d8b
optional agentops
bboynton97 Apr 4, 2024
59f5632
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 5, 2024
b20ae84
agentops version bump
bboynton97 Apr 5, 2024
de0ee8c
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 5, 2024
e82149a
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 11, 2024
2af85c3
remove org key
bboynton97 Apr 15, 2024
4f7a9a5
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 17, 2024
e52e8e9
true dependency
bboynton97 Apr 17, 2024
0ce8d14
add crew org key to agentops
bboynton97 Apr 17, 2024
7f5635f
cleanup
bboynton97 Apr 18, 2024
216cc83
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 18, 2024
906a5bd
Update pyproject.toml
joaomdmoura Apr 20, 2024
4d1b460
Revert "true dependency"
bboynton97 Apr 20, 2024
cea33d9
Revert "cleanup"
bboynton97 Apr 20, 2024
7f8573e
Merge remote-tracking branch 'origin/main'
bboynton97 Apr 20, 2024
7725e7c
optional parent key
bboynton97 Apr 20, 2024
c52b5e9
agentops 0.1.5
bboynton97 Apr 20, 2024
fd7de7f
Revert "Revert "cleanup""
bboynton97 Apr 20, 2024
e9335e8
Revert "Revert "true dependency""
bboynton97 Apr 20, 2024
9005dc7
cleanup
bboynton97 Apr 20, 2024
d678190
Forcing version 0.1.5
joaomdmoura Apr 20, 2024
e366f00
Update pyproject.toml
joaomdmoura Apr 20, 2024
8be18c8
agentops update
bboynton97 Apr 20, 2024
2f78980
Revert "Revert "Revert "true dependency"""
bboynton97 Apr 30, 2024
a19a37b
noop
bboynton97 Apr 30, 2024
b160a52
Merge remote-tracking branch 'upstream/main'
bboynton97 Apr 30, 2024
f2c2a62
add crew tag
bboynton97 May 2, 2024
be91c32
Merge remote-tracking branch 'upstream/main'
bboynton97 May 2, 2024
498bf77
black formatting
bboynton97 May 2, 2024
b9d6ec5
use langchain callback handler to support all LLMs
bboynton97 May 3, 2024
a6de525
Merge remote-tracking branch 'upstream/main'
bboynton97 May 6, 2024
b4241a8
agentops version bump
bboynton97 May 7, 2024
54237c9
track task evaluator
bboynton97 May 9, 2024
2d88109
Merge branch 'main' of https://github.com/joaomdmoura/crewAI
HowieG May 21, 2024
c647181
merge upstream
bboynton97 May 28, 2024
f4ce482
Fix typo in instruction en.json (#676)
OlivierRoberdet May 24, 2024
96e0dac
Enable search in docs (#663)
sanders41 May 24, 2024
a08d0df
Clarify text in docstring (#662)
sanders41 May 24, 2024
9ac6752
Update agent.py (#655)
Anudeep-Kolluri May 24, 2024
8e5bfac
Update README.md (#652)
heaversm May 24, 2024
b153bc1
Update BrowserbaseLoadTool.md (#647)
mishushakov May 24, 2024
3bba04a
Update crew.py (#644)
theCyberTech May 24, 2024
e2dfba6
fixes #665 (#666)
vmsaif May 26, 2024
0dd4f44
Added timestamp to logger (#646)
theCyberTech May 26, 2024
a4622bf
support skip auto end session
bboynton97 May 29, 2024
f516fba
Merge branch 'main' into main
bboynton97 Jun 7, 2024
ae65622
Merge remote-tracking branch 'upstream/main'
bboynton97 Jun 7, 2024
f2d2804
Merge remote-tracking branch 'origin/main'
bboynton97 Jun 7, 2024
45655a9
conditional protect agentops use
bboynton97 Jun 7, 2024
9b965d9
fix crew logger bug
bboynton97 Jun 7, 2024
fa9a42c
fix crew logger bug
bboynton97 Jun 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ click = "^8.1.7"
python-dotenv = "^1.0.0"
embedchain = "^0.1.98"
appdirs = "^1.4.4"
agentops = { version = "^0.1.9", optional = true }

[tool.poetry.extras]
tools = ["crewai-tools"]
agentops = ["agentops"]

[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
Expand Down
22 changes: 22 additions & 0 deletions src/crewai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@
from crewai.utilities import I18N, Logger, Prompts, RPMController
from crewai.utilities.token_counter_callback import TokenCalcHandler, TokenProcess

agentops = None
try:
import agentops
from agentops import track_agent
except ImportError:

def track_agent():
def noop(f):
return f

return noop


@track_agent()
class Agent(BaseModel):
"""Represents an agent in a system.

Expand Down Expand Up @@ -55,6 +68,8 @@ class Agent(BaseModel):
_rpm_controller: RPMController = PrivateAttr(default=None)
_request_within_rpm_limit: Any = PrivateAttr(default=None)
_token_process: TokenProcess = TokenProcess()
agent_ops_agent_name: str = None
agent_ops_agent_id: str = None

formatting_errors: int = 0
model_config = ConfigDict(arbitrary_types_allowed=True)
Expand Down Expand Up @@ -138,6 +153,7 @@ class Agent(BaseModel):
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
super().__init__(**config, **data)
__pydantic_self__.agent_ops_agent_name = __pydantic_self__.role

@field_validator("id", mode="before")
@classmethod
Expand Down Expand Up @@ -181,6 +197,12 @@ def set_agent_executor(self) -> "Agent":
):
self.llm.callbacks.append(token_handler)

if agentops and not any(
isinstance(handler, agentops.LangchainCallbackHandler) for handler in self.llm.callbacks
):
agentops.stop_instrumenting()
self.llm.callbacks.append(agentops.LangchainCallbackHandler())

if not self.agent_executor:
if not self.cache_handler:
self.cache_handler = CacheHandler()
Expand Down
12 changes: 12 additions & 0 deletions src/crewai/crew.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
from crewai.tools.agent_tools import AgentTools
from crewai.utilities import I18N, FileHandler, Logger, RPMController

try:
import agentops
except ImportError:
agentops = None


class Crew(BaseModel):
"""
Expand Down Expand Up @@ -248,6 +253,9 @@ def kickoff(self, inputs: Optional[Dict[str, Any]] = {}) -> str:
self._set_tasks_callbacks()

i18n = I18N(prompt_file=self.prompt_file)
if agentops:
agentops.set_parent_key("daebe730-f54d-4af5-98df-e6946fb76d13")
agentops.add_tags(["crewai"])

for agent in self.agents:
agent.i18n = i18n
Expand Down Expand Up @@ -389,6 +397,10 @@ def _format_output(self, output: str) -> str:
def _finish_execution(self, output) -> None:
if self.max_rpm:
self._rpm_controller.stop_rpm_counter()
if agentops:
agentops.end_session(
end_state="Success", end_state_reason="Finished Execution", is_auto_end=True
)
self._telemetry.end_crew(self, output)

def __repr__(self):
Expand Down
27 changes: 18 additions & 9 deletions src/crewai/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,16 @@ def execute( # type: ignore # Missing return statement
)

if self.context:
context = [] # type: ignore # Incompatible types in assignment (expression has type "list[Never]", variable has type "str | None")
# type: ignore # Incompatible types in assignment (expression has type "list[Never]", variable has type "str | None")
context = []
for task in self.context:
if task.async_execution:
task.thread.join() # type: ignore # Item "None" of "Thread | None" has no attribute "join"
if task and task.output:
context.append(task.output.raw_output) # type: ignore # Item "str" of "str | None" has no attribute "append"
context = "\n".join(context) # type: ignore # Argument 1 to "join" of "str" has incompatible type "str | None"; expected "Iterable[str]"
# type: ignore # Item "str" of "str | None" has no attribute "append"
context.append(task.output.raw_output)
# type: ignore # Argument 1 to "join" of "str" has incompatible type "str | None"; expected "Iterable[str]"
context = "\n".join(context)

self.prompt_context = context
tools = tools or self.tools
Expand Down Expand Up @@ -251,7 +254,8 @@ def _export_output(self, result: str) -> Any:

# try to convert task_output directly to pydantic/json
try:
exported_result = model.model_validate_json(result) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
# type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
exported_result = model.model_validate_json(result)
if self.output_json:
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result
Expand All @@ -260,17 +264,20 @@ def _export_output(self, result: str) -> Any:
match = re.search(r"({.*})", result, re.DOTALL)
if match:
try:
exported_result = model.model_validate_json(match.group(0)) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
# type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
exported_result = model.model_validate_json(match.group(0))
if self.output_json:
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result
except Exception:
pass

llm = self.agent.function_calling_llm or self.agent.llm # type: ignore # Item "None" of "Agent | None" has no attribute "function_calling_llm"
# type: ignore # Item "None" of "Agent | None" has no attribute "function_calling_llm"
llm = self.agent.function_calling_llm or self.agent.llm

if not self._is_gpt(llm):
model_schema = PydanticSchemaParser(model=model).get_schema() # type: ignore # Argument "model" to "PydanticSchemaParser" has incompatible type "type[BaseModel] | None"; expected "type[BaseModel]"
# type: ignore # Argument "model" to "PydanticSchemaParser" has incompatible type "type[BaseModel] | None"; expected "type[BaseModel]"
model_schema = PydanticSchemaParser(model=model).get_schema()
instructions = f"{instructions}\n\nThe json should have the following structure, with the following keys:\n{model_schema}"

converter = Converter(
Expand Down Expand Up @@ -301,12 +308,14 @@ def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None

def _save_file(self, result: Any) -> None:
directory = os.path.dirname(self.output_file) # type: ignore # Value of type variable "AnyOrLiteralStr" of "dirname" cannot be "str | None"
# type: ignore # Value of type variable "AnyOrLiteralStr" of "dirname" cannot be "str | None"
directory = os.path.dirname(self.output_file)

if directory and not os.path.exists(directory):
os.makedirs(directory)

with open(self.output_file, "w", encoding="utf-8") as file: # type: ignore # Argument 1 to "open" has incompatible type "str | None"; expected "int | str | bytes | PathLike[str] | PathLike[bytes]"
# type: ignore # Argument 1 to "open" has incompatible type "str | None"; expected "int | str | bytes | PathLike[str] | PathLike[bytes]"
with open(self.output_file, "w", encoding='utf-8') as file:
file.write(result)
return None

Expand Down
37 changes: 29 additions & 8 deletions src/crewai/tools/tool_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
from crewai.utilities import I18N, Converter, ConverterError, Printer

agentops = None
try:
import agentops
except ImportError:
pass

OPENAI_BIGGER_MODELS = ["gpt-4"]


Expand Down Expand Up @@ -91,15 +97,18 @@ def use(
self.task.increment_tools_errors()
self._printer.print(content=f"\n\n{error}\n", color="red")
return error
return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}" # type: ignore # BUG?: "_use" of "ToolUsage" does not return a value (it only ever returns None)
# type: ignore # BUG?: "_use" of "ToolUsage" does not return a value (it only ever returns None)
return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}"

def _use(
self,
tool_string: str,
tool: BaseTool,
calling: Union[ToolCalling, InstructorToolCalling],
) -> None: # TODO: Fix this return type
if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
tool_event = agentops.ToolEvent(name=calling.tool_name) if agentops else None
# type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
if self._check_tool_repeated_usage(calling=calling):
try:
result = self._i18n.errors("task_repeated_usage").format(
tool_names=self.tools_names
Expand All @@ -110,13 +119,15 @@ def _use(
tool_name=tool.name,
attempts=self._run_attempts,
)
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
# type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
result = self._format_result(result=result)
return result # type: ignore # Fix the reutrn type of this function

except Exception:
self.task.increment_tools_errors()

result = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
# type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
result = None

if self.tools_handler.cache:
result = self.tools_handler.cache.read( # type: ignore # Incompatible types in assignment (expression has type "str | None", variable has type "str")
Expand All @@ -133,7 +144,8 @@ def _use(

if calling.arguments:
try:
acceptable_args = tool.args_schema.schema()["properties"].keys() # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "schema"
# type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "schema"
acceptable_args = tool.args_schema.schema()["properties"].keys()
arguments = {
k: v
for k, v in calling.arguments.items()
Expand All @@ -145,7 +157,8 @@ def _use(
arguments = calling.arguments
result = tool._run(**arguments)
else:
arguments = calling.arguments.values() # type: ignore # Incompatible types in assignment (expression has type "dict_values[str, Any]", variable has type "dict[str, Any]")
# type: ignore # Incompatible types in assignment (expression has type "dict_values[str, Any]", variable has type "dict[str, Any]")
arguments = calling.arguments.values()
result = tool._run(*arguments)
else:
result = tool._run()
Expand All @@ -164,6 +177,10 @@ def _use(
return error # type: ignore # No return value expected

self.task.increment_tools_errors()
if agentops:
agentops.record(
agentops.ErrorEvent(exception=e, trigger_event=tool_event)
)
return self.use(calling=calling, tool_string=tool_string) # type: ignore # No return value expected

if self.tools_handler:
Expand All @@ -184,18 +201,22 @@ def _use(
)

self._printer.print(content=f"\n\n{result}\n", color="purple")
if agentops:
agentops.record(tool_event)
self._telemetry.tool_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
attempts=self._run_attempts,
)
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
# type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
result = self._format_result(result=result)
return result # type: ignore # No return value expected

def _format_result(self, result: Any) -> None:
self.task.used_tools += 1
if self._should_remember_format(): # type: ignore # "_should_remember_format" of "ToolUsage" does not return a value (it only ever returns None)
result = self._remember_format(result=result) # type: ignore # "_remember_format" of "ToolUsage" does not return a value (it only ever returns None)
# type: ignore # "_remember_format" of "ToolUsage" does not return a value (it only ever returns None)
result = self._remember_format(result=result)
return result

def _should_remember_format(self) -> None:
Expand Down
12 changes: 12 additions & 0 deletions src/crewai/utilities/evaluators/task_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@

from crewai.utilities import Converter
from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser
agentops = None
try:
import agentops
from agentops import track_agent
except ImportError:

def track_agent(name):
def noop(f):
return f

return noop


class Entity(BaseModel):
Expand All @@ -26,6 +37,7 @@ class TaskEvaluation(BaseModel):
)


@track_agent(name="Task Evaluator")
class TaskEvaluator:
def __init__(self, original_agent):
self.llm = original_agent.llm
Expand Down
2 changes: 1 addition & 1 deletion src/crewai/utilities/logger.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from crewai.utilities.printer import Printer

from datetime import datetime

class Logger:
_printer = Printer()
Expand Down