diff --git a/motleycrew/agent/crewai/crewai.py b/motleycrew/agent/crewai/crewai.py index d99fd533..cb186cc7 100644 --- a/motleycrew/agent/crewai/crewai.py +++ b/motleycrew/agent/crewai/crewai.py @@ -1,7 +1,9 @@ -from typing import Any, Optional, Sequence +from typing import Any, Optional, Sequence, Callable, Dict, List from crewai import Agent from langchain_core.runnables import RunnableConfig +from langchain.tools.render import render_text_description +from pydantic import Field from motleycrew.agent.parent import MotleyAgentAbstractParent from motleycrew.agent.shared import MotleyAgentParent @@ -12,6 +14,59 @@ from motleycrew.common.utils import to_str from motleycrew.common import LLMFramework from motleycrew.common.llms import init_llm +from motleycrew.tracking import add_default_callbacks_to_langchain_config + + +class CrewAIAgentWithConfig(Agent): + + def execute_task( + self, + task: Any, + context: Optional[str] = None, + tools: Optional[List[Any]] = None, + config: Optional[RunnableConfig] = None + ) -> str: + """Execute a task with the agent. + + Args: + task: Task to execute. + context: Context to execute the task in. + tools: Tools to use for the task. + config: Runnable config + Returns: + Output of the agent + """ + task_prompt = task.prompt() + + if context: + task_prompt = self.i18n.slice("task_with_context").format( + task=task_prompt, context=context + ) + + tools = self._parse_tools(tools or self.tools) + self.create_agent_executor(tools=tools) + self.agent_executor.tools = tools + self.agent_executor.task = task + self.agent_executor.tools_description = render_text_description(tools) + self.agent_executor.tools_names = self.__tools_names(tools) + + result = self.agent_executor.invoke( + { + "input": task_prompt, + "tool_names": self.agent_executor.tools_names, + "tools": self.agent_executor.tools_description, + }, + config=config + )["output"] + + if self.max_rpm: + self._rpm_controller.stop_rpm_counter() + + return result + + @staticmethod + def __tools_names(tools) -> str: + return ", ".join([t.name for t in tools]) class CrewAIMotleyAgentParent(MotleyAgentParent): @@ -61,8 +116,10 @@ def invoke( raise ValueError(f"`task` must be a string or a Task, not {type(task)}") langchain_tools = [tool.to_langchain_tool() for tool in self.tools.values()] + config = add_default_callbacks_to_langchain_config(config) + out = self.agent.execute_task( - task, to_str(task.message_history), tools=langchain_tools + task, to_str(task.message_history), tools=langchain_tools, config=config ) task.outputs = [out] # TODO: extract message history from agent, attach it to the task @@ -94,7 +151,7 @@ def from_crewai_params( def agent_factory(tools: dict[str, MotleyTool]): langchain_tools = [t.to_langchain_tool() for t in tools.values()] - agent = Agent( + agent = CrewAIAgentWithConfig( role=role, goal=goal, backstory=backstory, @@ -116,7 +173,7 @@ def agent_factory(tools: dict[str, MotleyTool]): @staticmethod def from_agent( - agent: Agent, + agent: CrewAIAgentWithConfig, delegation: bool | Sequence[MotleyAgentAbstractParent] = False, tools: Sequence[MotleySupportedTool] | None = None, verbose: bool = False, diff --git a/motleycrew/agent/langchain/langchain.py b/motleycrew/agent/langchain/langchain.py index 5747a186..1f098ee5 100644 --- a/motleycrew/agent/langchain/langchain.py +++ b/motleycrew/agent/langchain/langchain.py @@ -10,6 +10,7 @@ from motleycrew.tasks import Task from motleycrew.tool import MotleyTool +from motleycrew.tracking import add_default_callbacks_to_langchain_config from motleycrew.common import MotleySupportedTool from motleycrew.common import MotleyAgentFactory from motleycrew.common import LLMFramework @@ -44,6 +45,7 @@ def invoke( self.materialize() self.agent: AgentExecutor + config = add_default_callbacks_to_langchain_config(config) if isinstance(task, str): assert self.crew, "can't create a task outside a crew" # TODO: feed in context/task.message_history correctly diff --git a/motleycrew/agent/llama_index/llama_index_react.py b/motleycrew/agent/llama_index/llama_index_react.py index 0f8ebb68..5c8ae3ac 100644 --- a/motleycrew/agent/llama_index/llama_index_react.py +++ b/motleycrew/agent/llama_index/llama_index_react.py @@ -1,6 +1,7 @@ from typing import Sequence from llama_index.core.agent import ReActAgent from llama_index.core.llms import LLM +from llama_index.core.callbacks import CallbackManager from motleycrew.agent.llama_index import LlamaIndexMotleyAgentParent from motleycrew.agent.parent import MotleyAgentAbstractParent @@ -8,26 +9,31 @@ from motleycrew.common import MotleySupportedTool from motleycrew.common import LLMFramework from motleycrew.common.llms import init_llm +from motleycrew.tracking import get_default_callbacks_list class ReActLlamaIndexMotleyAgent(LlamaIndexMotleyAgentParent): - def __init__(self, - goal: str, - name: str | None = None, - delegation: bool | Sequence[MotleyAgentAbstractParent] = False, - tools: Sequence[MotleySupportedTool] | None = None, - llm: LLM | None = None, - verbose: bool = False): + def __init__( + self, + goal: str, + name: str | None = None, + delegation: bool | Sequence[MotleyAgentAbstractParent] = False, + tools: Sequence[MotleySupportedTool] | None = None, + llm: LLM | None = None, + verbose: bool = False, + ): if llm is None: llm = init_llm(llm_framework=LLMFramework.LLAMA_INDEX) def agent_factory(tools: dict[str, MotleyTool]): llama_index_tools = [t.to_llama_index_tool() for t in tools.values()] # TODO: feed goal into the agent's prompt + callbacks = get_default_callbacks_list(LLMFramework.LLAMA_INDEX) agent = ReActAgent.from_tools( tools=llama_index_tools, llm=llm, verbose=verbose, + callback_manager=CallbackManager(callbacks), ) return agent @@ -37,5 +43,5 @@ def agent_factory(tools: dict[str, MotleyTool]): agent_factory=agent_factory, delegation=delegation, tools=tools, - verbose=verbose + verbose=verbose, ) diff --git a/motleycrew/common/enums.py b/motleycrew/common/enums.py index 740221e5..7f611d0f 100644 --- a/motleycrew/common/enums.py +++ b/motleycrew/common/enums.py @@ -5,3 +5,17 @@ class LLMFamily: class LLMFramework: LANGCHAIN = "langchain" LLAMA_INDEX = "llama_index" + + +class LunaryRunType: + LLM = "llm" + AGENT = "agent" + TOOL = "tool" + CHAIN = "chain" + EMBED = "embed" + + +class LunaryEventName: + START = "start" + END = "end" + ERROR = "error" diff --git a/motleycrew/tracking/__init__.py b/motleycrew/tracking/__init__.py new file mode 100644 index 00000000..1a13b358 --- /dev/null +++ b/motleycrew/tracking/__init__.py @@ -0,0 +1 @@ +from .utils import add_default_callbacks_to_langchain_config, get_default_callbacks_list diff --git a/motleycrew/tracking/callbacks.py b/motleycrew/tracking/callbacks.py new file mode 100644 index 00000000..dd92cbf0 --- /dev/null +++ b/motleycrew/tracking/callbacks.py @@ -0,0 +1,265 @@ +from typing import List, Dict, Optional, Any, Union +import logging +import traceback + +from llama_index.core.callbacks.base_handler import BaseCallbackHandler +from llama_index.core.callbacks.schema import CBEventType, EventPayload +from llama_index.core.base.llms.types import ChatMessage +from lunary import track_event +from lunary.event_queue import EventQueue +from lunary.consumer import Consumer + +from motleycrew.common.enums import LunaryRunType, LunaryEventName + + +def event_delegate_decorator(f): + def wrapper(self, *args, **kwargs): + run_type = "start" if "start" in f.__name__ else "end" + + # find event type + if args: + event_type = args[0] + else: + event_type = kwargs.get("event_type", None) + + if not [event for event in CBEventType if event.value == event_type]: + return f(*args, **kwargs) + + # find and call handler + handler_name = "_on_{}_{}".format(event_type, run_type) + handler = getattr(self, handler_name, None) + if handler is not None and callable(handler): + handler_args = list(args)[1:] + try: + event_params = handler(*handler_args, **kwargs) + run_id = event_params.get("run_id") + run_type = event_params.get("run_type") + self._track_event(**event_params) + self._event_run_type_ids.append((run_type, run_id)) + except Exception as e: + msg = "[Lunary] An error occurred in {}: {}\n{}".format( + handler_name, e, traceback.format_exc() + ) + logging.warning(msg) + else: + msg = "No handler with the name of the {} was found for {}".format( + handler_name, self.__class__ + ) + logging.warning(msg) + + return f(*args, **kwargs) + + return wrapper + + +def _message_to_dict(message: ChatMessage) -> Dict[str, Any]: + """Creates and returns a dict based on the message""" + keys = ["function_call", "tool_calls", "tool_call_id", "name"] + output = {"content": message.content, "role": message.role.value} + output.update( + { + key: message.additional_kwargs.get(key) + for key in keys + if message.additional_kwargs.get(key) is not None + } + ) + return output + + +class LlamaIndexLunaryCallbackHandler(BaseCallbackHandler): + + AGENT_NAME = "LlamaIndexAgent" + + def __init__( + self, + app_id: str, + event_starts_to_ignore: List[CBEventType] = [], + event_ends_to_ignore: List[CBEventType] = [], + ): + super(LlamaIndexLunaryCallbackHandler, self).__init__( + event_starts_to_ignore=event_starts_to_ignore, + event_ends_to_ignore=event_ends_to_ignore, + ) + + self.__app_id = app_id + self._track_event = track_event + self._event_run_type_ids = [] + + self.queue = EventQueue() + self.consumer = Consumer(self.queue, self.__app_id) + self.consumer.start() + + def _get_initial_track_event_params( + self, run_type: LunaryRunType, event_name: LunaryEventName, run_id: str = None + ) -> dict: + """Return initial params for track event""" + params = { + "run_type": run_type, + "event_name": event_name, + "app_id": self.__app_id, + "callback_queue": self.queue, + } + if run_id is not None: + params["run_id"] = run_id + return params + + def _on_llm_start( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + parent_id: str = "", + **kwargs: Any, + ) -> dict: + messages = payload.get(EventPayload.MESSAGES) + serialized = payload.get(EventPayload.SERIALIZED) + + params = self._get_initial_track_event_params( + LunaryRunType.LLM, LunaryEventName.START, event_id + ) + params["name"] = serialized.get("model") + + tag = serialized.get("class_name") + if tag is not None: + params["tags"] = [tag] + + params["parent_run_id"] = self.check_parent_id(parent_id) + params["input"] = [_message_to_dict(message) for message in messages] + return params + + def _on_llm_end( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + **kwargs: Any, + ) -> dict: + response = payload.get(EventPayload.RESPONSE) + + params = self._get_initial_track_event_params( + LunaryRunType.LLM, LunaryEventName.END, event_id + ) + params["output"] = _message_to_dict(response.message) + return params + + def _on_function_call_start( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + parent_id: str = "", + **kwargs: Any, + ) -> dict: + tool = payload.get(EventPayload.TOOL) + function_call = payload.get(EventPayload.FUNCTION_CALL) + + params = self._get_initial_track_event_params( + LunaryRunType.TOOL, LunaryEventName.START, event_id + ) + + params["parent_run_id"] = self.check_parent_id(parent_id) + params["name"] = tool.name + params["input"] = '{{"query":"{}"}}'.format(function_call.get("query")) + + return params + + def _on_function_call_end( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + **kwargs: Any, + ) -> dict: + params = self._get_initial_track_event_params( + LunaryRunType.TOOL, LunaryEventName.END, event_id + ) + params["output"] = payload.get(EventPayload.FUNCTION_OUTPUT) + return params + + def _on_agent_step_start( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + parent_id: str = "", + **kwargs: Any, + ) -> dict: + messages = payload.get(EventPayload.MESSAGES) + + params = self._get_initial_track_event_params( + LunaryRunType.AGENT, LunaryEventName.START, event_id + ) + params["parent_run_id"] = self.check_parent_id(parent_id) + params["input"] = "\n".join(messages) + params["name"] = self.AGENT_NAME + + return params + + def _on_agent_step_end( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + **kwargs: Any, + ) -> dict: + response = payload.get(EventPayload.RESPONSE) + params = self._get_initial_track_event_params( + LunaryRunType.AGENT, LunaryEventName.END, event_id + ) + params["output"] = response.response + + return params + + def _on_exception_start( + self, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + parent_id: str = "", + **kwargs: Any, + ) -> dict: + + if self._event_run_type_ids: + run_type, run_id = self._event_run_type_ids[-1] + else: + run_type = LunaryRunType.AGENT + parent_id = self.check_parent_id(parent_id) + run_id = parent_id if parent_id else event_id + + _exception = payload.get(EventPayload.EXCEPTION) + params = self._get_initial_track_event_params( + run_type, LunaryEventName.ERROR, run_id + ) + params["error"] = {"message": str(_exception), "stack": traceback.format_exc()} + return params + + def start_trace(self, trace_id: Optional[str] = None) -> None: + pass + + def end_trace( + self, + trace_id: Optional[str] = None, + trace_map: Optional[Dict[str, List[str]]] = None, + ) -> None: + pass + + @event_delegate_decorator + def on_event_start( + self, + event_type: CBEventType, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + parent_id: str = "", + **kwargs: Any, + ) -> str: + return event_id + + @event_delegate_decorator + def on_event_end( + self, + event_type: CBEventType, + payload: Optional[Dict[str, Any]] = None, + event_id: str = "", + **kwargs: Any, + ) -> None: + return + + @staticmethod + def check_parent_id(parent_id: str) -> Union[str, None]: + """Checking the occurrence of a value in the ignored list""" + if parent_id in ["root"]: + return None + return parent_id diff --git a/motleycrew/tracking/utils.py b/motleycrew/tracking/utils.py new file mode 100644 index 00000000..0c85ded6 --- /dev/null +++ b/motleycrew/tracking/utils.py @@ -0,0 +1,109 @@ +import os +from typing import List, Optional +import logging + +from lunary import LunaryCallbackHandler +from langchain_core.callbacks import BaseCallbackHandler +from langchain_core.runnables import RunnableConfig, ensure_config + +from .callbacks import LlamaIndexLunaryCallbackHandler +from motleycrew.common import LLMFramework + + +def get_lunary_public_key(): + """Return lunary public key or None""" + return ( + os.environ.get("LUNARY_PUBLIC_KEY") + or os.getenv("LUNARY_APP_ID") + or os.getenv("LLMONITOR_APP_ID") + ) + + +def create_lunary_callback() -> LunaryCallbackHandler: + """Creation new LunaryCallbackHandler object""" + lunary_public_key = get_lunary_public_key() + if lunary_public_key is not None: + return LunaryCallbackHandler(app_id=lunary_public_key) + + +def get_llamaindex_default_callbacks(): + """Return tracking for llamaindex platform""" + _default_callbacks = [] + + # init lunary callback + lunary_public_key = get_lunary_public_key() + if lunary_public_key is not None: + _default_callbacks.append(LlamaIndexLunaryCallbackHandler(lunary_public_key)) + + return _default_callbacks + + +def get_langchain_default_callbacks(): + """Return tracking for langchain platform""" + _default_callbacks = [] + + # init lunary callback + lunary_callback = create_lunary_callback() + if lunary_callback is not None: + _default_callbacks.append(lunary_callback) + + return _default_callbacks + + +DEFAULT_CALLBACKS_MAP = { + LLMFramework.LANGCHAIN: get_langchain_default_callbacks, + LLMFramework.LLAMA_INDEX: get_llamaindex_default_callbacks, +} + + +def get_default_callbacks_list( + framework_name: str = LLMFramework.LANGCHAIN, +) -> List[BaseCallbackHandler]: + """Return list of defaults tracking""" + _default_callbacks = [] + dc_factory = DEFAULT_CALLBACKS_MAP.get(framework_name, None) + + if callable(dc_factory): + _default_callbacks = dc_factory() + else: + msg = "Default callbacks are not implemented for {} framework".format( + framework_name + ) + logging.warning(msg) + + return _default_callbacks + + +def combine_callbacks( + updated_callbacks: List[BaseCallbackHandler], + updating_callbacks: List[BaseCallbackHandler], + unique_cls: bool = True, +) -> List[BaseCallbackHandler]: + """Combining callback lists + unique_cls: bool - flag adding callback with a unique class + return : modified updated_callbacks list + """ + for updating in updating_callbacks: + if unique_cls and not any( + isinstance(updating, updated.__class__) for updated in updated_callbacks + ): + updated_callbacks.append(updating) + elif updating not in updating_callbacks: + updated_callbacks.append(updating) + return updated_callbacks + + +def add_default_callbacks_to_langchain_config( + config: Optional[RunnableConfig] = None, +) -> RunnableConfig: + """Add default callback to langchain config + return: modified config + """ + if config is None: + config = ensure_config(config) + + _default_callbacks = get_default_callbacks_list() + if _default_callbacks: + config_callbacks = config.get("tracking") or [] + config["tracking"] = combine_callbacks(config_callbacks, _default_callbacks) + return config diff --git a/poetry.lock b/poetry.lock index 76988042..d5f248a1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -414,6 +414,17 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "chevron" +version = "0.14.0" +description = "Mustache templating language renderer" +optional = false +python-versions = "*" +files = [ + {file = "chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443"}, + {file = "chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf"}, +] + [[package]] name = "click" version = "8.1.7" @@ -1189,6 +1200,21 @@ langchain-core = ">=0.1.28,<0.2.0" [package.extras] extended-testing = ["lxml (>=5.1.0,<6.0.0)"] +[[package]] +name = "langchainhub" +version = "0.1.15" +description = "The LangChain Hub API client" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchainhub-0.1.15-py3-none-any.whl", hash = "sha256:89a0951abd1db255e91c6d545d092a598fc255aa865d1ffc3ce8f93bbeae60e7"}, + {file = "langchainhub-0.1.15.tar.gz", hash = "sha256:fa3ff81a31946860f84c119f1e2f6b7c7707e2bd7ed2394a7313b286d59f3bda"}, +] + +[package.dependencies] +requests = ">=2,<3" +types-requests = ">=2.31.0.2,<3.0.0.0" + [[package]] name = "langsmith" version = "0.1.49" @@ -1499,6 +1525,27 @@ files = [ httpx = ">=0.20.0" pydantic = ">=1.10" +[[package]] +name = "lunary" +version = "1.0.3" +description = "Observability, analytics and evaluations for AI agents and chatbots." +optional = false +python-versions = "<4.0.0,>=3.8.1" +files = [ + {file = "lunary-1.0.3-py3-none-any.whl", hash = "sha256:f3ff4b85c589576541ae517bab46defed471853deab28fb85a0c90192d823984"}, + {file = "lunary-1.0.3.tar.gz", hash = "sha256:a760637b349ff0aa33d81e173385f209e165a988da16b50713d86242a8d94696"}, +] + +[package.dependencies] +chevron = ">=0.14.0,<0.15.0" +opentelemetry-api = ">=1.21.0,<2.0.0" +opentelemetry-sdk = ">=1.21.0,<2.0.0" +packaging = ">=23.2,<24.0" +pyhumps = ">=3.8.0,<4.0.0" +requests = ">=2.31.0,<3.0.0" +setuptools = ">=67.6.2,<68.0.0" +tenacity = ">=8.2.3,<9.0.0" + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2382,6 +2429,17 @@ files = [ plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + [[package]] name = "pypdf" version = "4.2.0" @@ -2457,6 +2515,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pytz" version = "2024.1" @@ -2671,19 +2743,19 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "69.5.1" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -2934,6 +3006,20 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2 doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +[[package]] +name = "types-requests" +version = "2.31.0.20240406" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, + {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, +] + +[package.dependencies] +urllib3 = ">=2" + [[package]] name = "typing-extensions" version = "4.11.0" @@ -3188,4 +3274,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.10,<=3.13" -content-hash = "b9bc96de238e70c5a99db3bc4face54cbdbf0be4e57682d8a0946644e5e3aa4e" +content-hash = "2b4c279975a9bf627bdd621f23b95cc401ee4bfa70be9acb7e1adea510e535e1" diff --git a/pyproject.toml b/pyproject.toml index 255cfafe..edbbe692 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,10 +9,13 @@ readme = "README.md" python = ">=3.10,<=3.13" langchain = "^0.1" crewai = "^0.16" -setuptools = "^69.2.0" +setuptools = "^67.6.2" duckduckgo-search = "^5.2.1" llama-index = "^0.10.27" langchain-experimental = "^0.0.57" +python-dotenv = "^1.0.0" +lunary = "^1.0.3" +langchainhub = "^0.1.15" [tool.poetry.group.dev.dependencies] black = "^24.2.0"