From b65c7a73f3f98549f30d140114eb345d1e7b0eb4 Mon Sep 17 00:00:00 2001 From: Dirk Brand <51947788+dirkbrnd@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:50:58 +0200 Subject: [PATCH] Release 2.7.5 (#1617) ## Description This update introduces the Confluence tool for collaboration, improves tool compatibility, and resolves deep copy issues for Ollama chat agents. **New Features:** - Confluence Tool: Added a new tool using the Atlassian Confluence SDK, enabling operations such as listing pages in a space, creating and updating pages, retrieving page content by title, and getting space details. **Improvements:** - Tool Compatibility: Enhanced older custom functions with manually specified descriptions and parameters to align with the updated tool-building system. **Bug Fixes:** - Deep Copy for Ollama Chat Agents: Addressed an issue where manually set clients caused errors during agent model copying, ensuring all properties are properly handled. --------- Co-authored-by: Anurag --- cookbook/agents/14_generate_image.py | 8 ++------ cookbook/chunking/__init__.py | 0 cookbook/mysql-init/init.sql | 16 ++++++++++++++++ cookbook/providers/ollama/agent_set_client.py | 18 ++++++++++++++++++ cookbook/readers/__init__.py | 0 cookbook/run_mysql.sh | 10 ++++++++++ cookbook/tools/composio_tools.py | 1 - phi/model/ollama/chat.py | 4 ++++ phi/tools/function.py | 13 ++++++++++--- pyproject.toml | 2 +- 10 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 cookbook/chunking/__init__.py create mode 100644 cookbook/mysql-init/init.sql create mode 100644 cookbook/providers/ollama/agent_set_client.py create mode 100644 cookbook/readers/__init__.py create mode 100755 cookbook/run_mysql.sh diff --git a/cookbook/agents/14_generate_image.py b/cookbook/agents/14_generate_image.py index 1f8323155..642d62710 100644 --- a/cookbook/agents/14_generate_image.py +++ b/cookbook/agents/14_generate_image.py @@ -16,9 +16,5 @@ images = image_agent.get_images() if images and isinstance(images, list): for image_response in images: - image_data = image_response.get("data") # type: ignore - if image_data: - for image in image_data: - image_url = image.get("url") # type: ignore - if image_url: - print(image_url) + image_url = image_response.url + print(image_url) diff --git a/cookbook/chunking/__init__.py b/cookbook/chunking/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cookbook/mysql-init/init.sql b/cookbook/mysql-init/init.sql new file mode 100644 index 000000000..398c886a0 --- /dev/null +++ b/cookbook/mysql-init/init.sql @@ -0,0 +1,16 @@ +-- Create 'users' table +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + email VARCHAR(100) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Create 'products' table +CREATE TABLE IF NOT EXISTS products ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + description TEXT, + price DECIMAL(10,2) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/cookbook/providers/ollama/agent_set_client.py b/cookbook/providers/ollama/agent_set_client.py new file mode 100644 index 000000000..3377811e6 --- /dev/null +++ b/cookbook/providers/ollama/agent_set_client.py @@ -0,0 +1,18 @@ +"""Run `pip install yfinance` to install dependencies.""" + +from ollama import Client as OllamaClient +from phi.agent import Agent, RunResponse # noqa +from phi.model.ollama import Ollama +from phi.playground import Playground, serve_playground_app +from phi.tools.yfinance import YFinanceTools + +agent = Agent( + model=Ollama(id="llama3.1:8b", client=OllamaClient()), + tools=[YFinanceTools(stock_price=True)], + markdown=True, +) + +app = Playground(agents=[agent]).get_app() + +if __name__ == "__main__": + serve_playground_app("agent_set_client:app", reload=True) diff --git a/cookbook/readers/__init__.py b/cookbook/readers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cookbook/run_mysql.sh b/cookbook/run_mysql.sh new file mode 100755 index 000000000..908347566 --- /dev/null +++ b/cookbook/run_mysql.sh @@ -0,0 +1,10 @@ +docker run -d \ + -e MYSQL_ROOT_PASSWORD=phi \ + -e MYSQL_DATABASE=phi \ + -e MYSQL_USER=phi \ + -e MYSQL_PASSWORD=phi \ + -p 3306:3306 \ + -v mysql_data:/var/lib/mysql \ + -v $(pwd)/cookbook/mysql-init:/docker-entrypoint-initdb.d \ + --name mysql \ + mysql:8.0 diff --git a/cookbook/tools/composio_tools.py b/cookbook/tools/composio_tools.py index 69650892b..9aa8575dc 100644 --- a/cookbook/tools/composio_tools.py +++ b/cookbook/tools/composio_tools.py @@ -1,7 +1,6 @@ from phi.agent import Agent from composio_phidata import Action, ComposioToolSet # type: ignore - toolset = ComposioToolSet() composio_tools = toolset.get_tools(actions=[Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER]) diff --git a/phi/model/ollama/chat.py b/phi/model/ollama/chat.py index acc744454..51f39089e 100644 --- a/phi/model/ollama/chat.py +++ b/phi/model/ollama/chat.py @@ -722,3 +722,7 @@ async def aresponse_stream(self, messages: List[Message]) -> Any: async for post_tool_call_response in self.ahandle_post_tool_call_messages_stream(messages=messages): yield post_tool_call_response logger.debug("---------- Ollama Async Response End ----------") + + def model_copy(self, *, update: Optional[dict[str, Any]] = None, deep: bool = False) -> "Ollama": + new_model = Ollama(**self.model_dump(exclude={"client"}), client=self.client) + return new_model diff --git a/phi/tools/function.py b/phi/tools/function.py index a8174e147..452fad808 100644 --- a/phi/tools/function.py +++ b/phi/tools/function.py @@ -53,7 +53,7 @@ class Function(BaseModel): # The parameters the functions accepts, described as a JSON Schema object. # To describe a function that accepts no parameters, provide the value {"type": "object", "properties": {}}. parameters: Dict[str, Any] = Field( - default_factory=lambda: {"type": "object", "properties": {}}, + default_factory=lambda: {"type": "object", "properties": {}, "required": []}, description="JSON Schema object describing function parameters", ) strict: Optional[bool] = None @@ -139,6 +139,12 @@ def process_entrypoint(self, strict: bool = False): return parameters = {"type": "object", "properties": {}, "required": []} + + params_set_by_user = False + # If the user set the parameters (i.e. they are different from the default), we should keep them + if self.parameters != parameters: + params_set_by_user = True + try: sig = signature(self.entrypoint) type_hints = get_type_hints(self.entrypoint) @@ -174,8 +180,9 @@ def process_entrypoint(self, strict: bool = False): except Exception as e: logger.warning(f"Could not parse args for {self.name}: {e}", exc_info=True) - self.description = getdoc(self.entrypoint) or self.description - self.parameters = parameters + self.description = self.description or getdoc(self.entrypoint) + if not params_set_by_user: + self.parameters = parameters self.entrypoint = validate_call(self.entrypoint) def get_type_name(self, t: Type[T]): diff --git a/pyproject.toml b/pyproject.toml index 1ca8e856e..f59e309b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "phidata" -version = "2.7.4" +version = "2.7.5" description = "Build multi-modal Agents with memory, knowledge and tools." requires-python = ">=3.7" readme = "README.md"