Skip to content

Commit

Permalink
v2.0.36
Browse files Browse the repository at this point in the history
  • Loading branch information
ashpreetbedi committed Nov 10, 2023
1 parent 569f114 commit 469fc0a
Show file tree
Hide file tree
Showing 43 changed files with 227 additions and 1,700 deletions.
13 changes: 7 additions & 6 deletions phi/ai/phi_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
from phi.cli.config import PhiCliConfig
from phi.cli.console import console
from phi.cli.settings import phi_cli_settings
from phi.llm.schemas import Function, Message, FunctionCall
from phi.llm.function.shell import ShellScriptsRegistry
from phi.llm.function.phi_commands import PhiCommandsRegistry
from phi.llm.schemas import Message
from phi.tool.function import Function, FunctionCall
from phi.tool.shell import ShellTool
from phi.tool.phi import PhiTool
from phi.workspace.config import WorkspaceConfig
from phi.utils.log import logger
from phi.utils.functions import get_function_call
Expand All @@ -41,7 +42,7 @@ def __init__(
_active_workspace = _phi_config.get_active_ws_config()

self.conversation_db: Optional[List[Dict[str, Any]]] = None
self.functions: Dict[str, Function] = {**ShellScriptsRegistry().functions, **PhiCommandsRegistry().functions}
self.functions: Dict[str, Function] = {**ShellTool().functions, **PhiTool().functions}

_conversation_id = None
_conversation_history = None
Expand Down Expand Up @@ -170,7 +171,7 @@ def run_function_stream(self, function_call: Dict[str, Any]) -> Iterator[str]:
yield f"Running: {function_call_obj.get_call_str()}\n\n"
function_call_timer = Timer()
function_call_timer.start()
function_call_obj.run()
function_call_obj.execute()
function_call_timer.stop()
function_call_message = Message(
role="function",
Expand Down Expand Up @@ -213,7 +214,7 @@ def run_function(self, function_call: Dict[str, Any]) -> str:
function_run_response = f"Running: {function_call_obj.get_call_str()}\n\n"
function_call_timer = Timer()
function_call_timer.start()
function_call_obj.run()
function_call_obj.execute()
function_call_timer.stop()
function_call_message = Message(
role="function",
Expand Down
3 changes: 2 additions & 1 deletion phi/api/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
ConversationCreateResponse,
)
from phi.api.schemas.user import UserSchema
from phi.llm.schemas import Function, Message
from phi.llm.schemas import Message
from phi.tool.function import Function
from phi.utils.log import logger


Expand Down
139 changes: 36 additions & 103 deletions phi/assistant/assistant.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import json
from typing import List, Any, Optional, Dict, Union, Callable

from pydantic import BaseModel, ConfigDict, field_validator, model_validator

from phi.assistant.file import File
from phi.assistant.tool import Tool
from phi.assistant.tool.registry import ToolRegistry
from phi.assistant.row import AssistantRow
from phi.assistant.function import Function, FunctionCall
from phi.assistant.storage import AssistantStorage
from phi.assistant.exceptions import AssistantIdNotSet
from phi.tool import Tool
from phi.tool.registry import ToolRegistry
from phi.tool.function import Function
from phi.knowledge.base import KnowledgeBase

from phi.utils.log import logger, set_log_level_to_debug

try:
Expand Down Expand Up @@ -43,8 +43,9 @@ class Assistant(BaseModel):
# A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant.
# Tools can be of types code_interpreter, retrieval, or function.
tools: Optional[List[Union[Tool, Dict, Callable, ToolRegistry]]] = None
# Functions the Assistant may call.
_function_map: Optional[Dict[str, Function]] = None
# -*- Functions available to the Assistant to call
# Functions provided from the tools. Note: These are not sent to the LLM API.
functions: Optional[Dict[str, Function]] = None

# -*- Assistant Files
# A list of file IDs attached to this assistant.
Expand Down Expand Up @@ -94,24 +95,19 @@ def set_log_level(cls, v: bool) -> bool:
def client(self) -> OpenAI:
return self.openai or OpenAI()

def add_function(self, f: Function) -> None:
if self._function_map is None:
self._function_map = {}
self._function_map[f.name] = f
logger.debug(f"Added function {f.name} to Assistant")

@model_validator(mode="after")
def add_functions_to_assistant(self) -> "Assistant":
def extract_functions_from_tools(self) -> "Assistant":
if self.tools is not None:
for tool in self.tools:
if callable(tool):
f = Function.from_callable(tool)
self.add_function(f)
elif isinstance(tool, ToolRegistry):
if self._function_map is None:
self._function_map = {}
self._function_map.update(tool.functions)
if self.functions is None:
self.functions = {}
if isinstance(tool, ToolRegistry):
self.functions.update(tool.functions)
logger.debug(f"Tools from {tool.name} added to Assistant.")
elif callable(tool):
f = Function.from_callable(tool)
self.functions[f.name] = f
logger.debug(f"Added function {f.name} to Assistant")
return self

def load_from_storage(self):
Expand All @@ -124,6 +120,24 @@ def load_from_openai(self, openai_assistant: OpenAIAssistant):
self.file_ids = openai_assistant.file_ids
self.openai_assistant = openai_assistant

def get_tools_for_api(self) -> Optional[List[Dict[str, Any]]]:
if self.tools is None:
return None

tools_for_api = []
for tool in self.tools:
if isinstance(tool, Tool):
tools_for_api.append(tool.to_dict())
elif isinstance(tool, dict):
tools_for_api.append(tool)
elif callable(tool):
func = Function.from_callable(tool)
tools_for_api.append({"type": "function", "function": func.to_dict()})
elif isinstance(tool, ToolRegistry):
for _f in tool.functions.values():
tools_for_api.append({"type": "function", "function": _f.to_dict()})
return tools_for_api

def create(self) -> "Assistant":
request_body: Dict[str, Any] = {}
if self.name is not None:
Expand All @@ -133,19 +147,7 @@ def create(self) -> "Assistant":
if self.instructions is not None:
request_body["instructions"] = self.instructions
if self.tools is not None:
_tools = []
for _tool in self.tools:
if isinstance(_tool, Tool):
_tools.append(_tool.to_dict())
elif isinstance(_tool, dict):
_tools.append(_tool)
elif callable(_tool):
func = Function.from_callable(_tool)
_tools.append({"type": "function", "function": func.to_dict()})
elif isinstance(_tool, ToolRegistry):
for _f in _tool.functions.values():
_tools.append({"type": "function", "function": _f.to_dict()})
request_body["tools"] = _tools
request_body["tools"] = self.get_tools_for_api()
if self.file_ids is not None or self.files is not None:
_file_ids = self.file_ids or []
if self.files is not None:
Expand Down Expand Up @@ -208,19 +210,7 @@ def update(self) -> "Assistant":
if self.instructions is not None:
request_body["instructions"] = self.instructions
if self.tools is not None:
_tools = []
for _tool in self.tools:
if isinstance(_tool, Tool):
_tools.append(_tool.to_dict())
elif isinstance(_tool, dict):
_tools.append(_tool)
elif callable(_tool):
func = Function.from_callable(_tool)
_tools.append({"type": "function", "function": func.to_dict()})
elif isinstance(_tool, ToolRegistry):
for _f in _tool.functions.values():
_tools.append({"type": "function", "function": _f.to_dict()})
request_body["tools"] = _tools
request_body["tools"] = self.get_tools_for_api()
if self.file_ids is not None or self.files is not None:
_file_ids = self.file_ids or []
if self.files is not None:
Expand Down Expand Up @@ -286,61 +276,4 @@ def pprint(self):
pprint(self.to_dict())

def __str__(self) -> str:
import json

return json.dumps(self.to_dict(), indent=4)

def get_function_call(self, name: str, arguments: Optional[str] = None) -> Optional[FunctionCall]:
import json

logger.debug(f"Getting function {name}. Args: {arguments}")
if self._function_map is None:
return None

function_to_call: Optional[Function] = None
if name in self._function_map:
function_to_call = self._function_map[name]
if function_to_call is None:
logger.error(f"Function {name} not found")
return None

function_call = FunctionCall(function=function_to_call)
if arguments is not None and arguments != "":
try:
if "None" in arguments:
arguments = arguments.replace("None", "null")
if "True" in arguments:
arguments = arguments.replace("True", "true")
if "False" in arguments:
arguments = arguments.replace("False", "false")
_arguments = json.loads(arguments)
except Exception as e:
logger.error(f"Unable to decode function arguments {arguments}: {e}")
return None

if not isinstance(_arguments, dict):
logger.error(f"Function arguments {arguments} is not a valid JSON object")
return None

try:
clean_arguments: Dict[str, Any] = {}
for k, v in _arguments.items():
if isinstance(v, str):
_v = v.strip().lower()
if _v in ("none", "null"):
clean_arguments[k] = None
elif _v == "true":
clean_arguments[k] = True
elif _v == "false":
clean_arguments[k] = False
else:
clean_arguments[k] = v.strip()
else:
clean_arguments[k] = v

function_call.arguments = clean_arguments
except Exception as e:
logger.error(f"Unable to parse function arguments {arguments}: {e}")
return None

return function_call
13 changes: 8 additions & 5 deletions phi/assistant/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

from pydantic import BaseModel, ConfigDict, model_validator

from phi.assistant.tool import Tool
from phi.assistant.tool.registry import ToolRegistry
from phi.assistant.function import Function
from phi.assistant.assistant import Assistant
from phi.assistant.exceptions import ThreadIdNotSet, AssistantIdNotSet, RunIdNotSet
from phi.tool import Tool
from phi.tool.registry import ToolRegistry
from phi.tool.function import Function
from phi.utils.functions import get_function_call
from phi.utils.log import logger

try:
Expand Down Expand Up @@ -300,8 +301,10 @@ def run(
tool_outputs = []
for tool_call in tool_calls:
if tool_call.type == "function":
function_call = self.assistant.get_function_call(
name=tool_call.function.name, arguments=tool_call.function.arguments
function_call = get_function_call(
name=tool_call.function.name,
arguments=tool_call.function.arguments,
functions=self.assistant.functions,
)
if function_call is None:
logger.error(f"Function {tool_call.function.name} not found")
Expand Down
1 change: 0 additions & 1 deletion phi/assistant/tool/__init__.py

This file was deleted.

Loading

0 comments on commit 469fc0a

Please sign in to comment.