diff --git a/cookbook/agents/telemetry.py b/cookbook/agents/telemetry.py index 2a4b0026b..918bddae0 100644 --- a/cookbook/agents/telemetry.py +++ b/cookbook/agents/telemetry.py @@ -14,7 +14,7 @@ storage=PgAgentStorage(table_name="agent_sessions", db_url="postgresql+psycopg://ai:ai@localhost:5532/ai"), ) -run1: RunResponse = agent.run("What is the stock price of NVDA") +run1: RunResponse = agent.run("What is the stock price of NVDA") # type: ignore # run2: RunResponse = agent.run({"text": "What is the stock price of NVDA", "image": "https://example.com/image.jpg"}) pprint(run1) # print("------------*******************------------") diff --git a/cookbook/providers/openai/basic_stream_off.py b/cookbook/providers/openai/basic_stream_off.py index 79fcb25fe..6c96d3609 100644 --- a/cookbook/providers/openai/basic_stream_off.py +++ b/cookbook/providers/openai/basic_stream_off.py @@ -5,6 +5,6 @@ model=OpenAIChat(model="gpt-4o"), ) -run: RunResponse = agent.run("Share a healthy breakfast recipe") +run: RunResponse = agent.run("Share a healthy breakfast recipe") # type: ignore print(run.content) diff --git a/phi/agent/agent.py b/phi/agent/agent.py index 79c9161fc..1e2856816 100644 --- a/phi/agent/agent.py +++ b/phi/agent/agent.py @@ -269,6 +269,37 @@ def get_delegation_prompt(self) -> str: return delegation_prompt return "" + def get_tools(self) -> Optional[List[Union[Tool, Toolkit, Callable, Dict, Function]]]: + tools: List[Union[Tool, Toolkit, Callable, Dict, Function]] = [] + + # Add provided tools + if self.tools is not None: + for tool in self.tools: + tools.append(tool) + + # Add tools for accessing memory + if self.memory is not None: + if self.read_chat_history: + tools.append(self.get_chat_history) + if self.read_tool_call_history: + tools.append(self.get_tool_call_history) + if self.create_memories: + tools.append(self.update_memory) + + # Add tools for accessing knowledge + if self.knowledge is not None: + if self.search_knowledge: + tools.append(self.search_knowledge_base) + if self.update_knowledge: + tools.append(self.add_to_knowledge) + + # Add delegation tools + if self.team is not None and len(self.team) > 0: + for agent_index, agent in enumerate(self.team): + tools.append(self.get_delegation_function(agent, agent_index)) + + return tools + def update_model(self) -> None: if self.model is None: try: @@ -286,32 +317,12 @@ def update_model(self) -> None: if self.output_model is not None and self.model.response_format is None: self.model.response_format = {"type": "json_object"} - # Add tools for accessing memory - if self.memory is not None: - if self.read_chat_history: - self.model.add_tool(self.get_chat_history) - if self.read_tool_call_history: - self.model.add_tool(self.get_tool_call_history) - if self.create_memories: - self.model.add_tool(self.update_memory) - - # Add tools for accessing knowledge - if self.knowledge is not None: - if self.search_knowledge: - self.model.add_tool(self.search_knowledge_base) - if self.update_knowledge: - self.model.add_tool(self.add_to_knowledge) - # Add tools to the Model - if self.tools is not None: - for tool in self.tools: + agent_tools = self.get_tools() + if agent_tools is not None: + for tool in agent_tools: self.model.add_tool(tool) - # Add delegation tools to the Model - if self.team is not None and len(self.team) > 0: - for agent_index, agent in enumerate(self.team): - self.model.add_tool(self.get_delegation_function(agent, agent_index)) - # Set show_tool_calls if it is not set on the Model if self.model.show_tool_calls is None and self.show_tool_calls is not None: self.model.show_tool_calls = self.show_tool_calls diff --git a/phi/playground/playground.py b/phi/playground/playground.py index fec6a7be4..16cf05156 100644 --- a/phi/playground/playground.py +++ b/phi/playground/playground.py @@ -1,16 +1,16 @@ -from typing import List, Optional, Generator, Iterator +from typing import List, Optional, Generator, Any, Dict, cast from fastapi import FastAPI, HTTPException from fastapi.routing import APIRouter from fastapi.responses import StreamingResponse from pydantic import BaseModel -from phi.agent.agent import Agent, RunResponse +from phi.agent.agent import Agent, RunResponse, Tool, Toolkit, Function from phi.playground.settings import PlaygroundSettings from phi.utils.log import logger -class AgentLLM(BaseModel): +class AgentModel(BaseModel): name: Optional[str] = None model: Optional[str] = None provider: Optional[str] = None @@ -18,11 +18,16 @@ class AgentLLM(BaseModel): class AgentGetResponse(BaseModel): agent_id: str - llm: Optional[AgentLLM] = None name: Optional[str] = None + model: Optional[AgentModel] = None + enable_rag: Optional[bool] = None + tools: Optional[List[Dict[str, Any]]] = None + storage: Optional[Dict[str, Any]] = None + knowledge: Optional[Dict[str, Any]] = None + details: Optional[Dict[str, Any]] = None -class AgentChatRequest(BaseModel): +class AgentRunRequest(BaseModel): message: str agent_id: str stream: bool = True @@ -36,14 +41,16 @@ def __init__( agents: List[Agent], settings: Optional[PlaygroundSettings] = None, api_app: Optional[FastAPI] = None, - api_router: Optional[APIRouter] = None, + router: Optional[APIRouter] = None, ): self.agents: List[Agent] = agents self.settings: PlaygroundSettings = settings or PlaygroundSettings() self.api_app: Optional[FastAPI] = api_app - self.api_router: Optional[APIRouter] = api_router + self.router: Optional[APIRouter] = router - def get_api_router(self): + self.agent_list: Optional[List[AgentGetResponse]] = None + + def get_router(self): playground_routes = APIRouter(prefix="/playground", tags=["Playground"]) @playground_routes.get("/status") @@ -52,31 +59,61 @@ def playground_status(): @playground_routes.get("/agent/get", response_model=List[AgentGetResponse]) def agent_get(): - agent_list: List[AgentGetResponse] = [] + if self.agent_list is not None: + return self.agent_list + + self.agent_list = [] for agent in self.agents: - agent_list.append( + agent_tools = agent.get_tools() + formatted_tools = [] + if agent_tools is not None: + for tool in agent_tools: + if isinstance(tool, dict): + formatted_tools.append(tool) + elif isinstance(tool, Tool): + formatted_tools.append(tool.to_dict()) + elif isinstance(tool, Toolkit): + for f_name, f in tool.functions.items(): + formatted_tools.append(f.to_dict()) + elif isinstance(tool, Function): + formatted_tools.append(tool.to_dict()) + elif callable(tool): + func = Function.from_callable(tool) + formatted_tools.append(func.to_dict()) + else: + logger.warning(f"Unknown tool type: {type(tool)}") + + self.agent_list.append( AgentGetResponse( - llm=AgentLLM( + agent_id=agent.agent_id, + name=agent.name, + model=AgentModel( provider=agent.model.provider or agent.model.__class__.__name__ if agent.model else None, name=agent.model.name or agent.model.__class__.__name__ if agent.model else None, model=agent.model.model if agent.model else None, ), - name=agent.name, - agent_id=agent.agent_id, + enable_rag=agent.enable_rag, + tools=formatted_tools, + storage={"name": agent.storage.__class__.__name__} if agent.storage else None, + knowledge={agent.knowledge.__class__.__name__} if agent.knowledge else None, + details={ + "description": agent.description, + "instructions": agent.instructions, + }, ) ) - return agent_list + return self.agent_list def chat_response_streamer(agent: Agent, message: str) -> Generator: - logger.info(f"ChatRequest: {message} for Agent: {agent.agent_id}") - run_response: Iterator[RunResponse] = agent.run(message, stream=True) + run_response = agent.run(message, stream=True) for run_response_chunk in run_response: - yield run_response_chunk.content + run_response_chunk = cast(RunResponse, run_response_chunk) + yield run_response_chunk.model_dump_json() - @playground_routes.post("/agent/chat") - def agent_chat(body: AgentChatRequest): - logger.debug(f"ChatRequest: {body}") + @playground_routes.post("/agent/run") + def agent_chat(body: AgentRunRequest): + logger.debug(f"AgentRunRequest: {body}") agent: Optional[Agent] = None for _agent in self.agents: if _agent.agent_id == body.agent_id: @@ -91,7 +128,8 @@ def agent_chat(body: AgentChatRequest): media_type="text/event-stream", ) else: - return agent.run(body.message, stream=False) + run_response = cast(RunResponse, agent.run(body.message, stream=False)) + return run_response.model_dump_json() return playground_routes @@ -116,14 +154,14 @@ def get_api_app(self) -> FastAPI: raise Exception("API App could not be created.") # Create an API Router if not provided - if not self.api_router: - self.api_router = APIRouter(prefix="/v1") + if not self.router: + self.router = APIRouter(prefix="/v1") - if not self.api_router: + if not self.router: raise Exception("API Router could not be created.") - self.api_router.include_router(self.get_api_router()) - self.api_app.include_router(self.api_router) + self.router.include_router(self.get_router()) + self.api_app.include_router(self.router) # Add Middlewares self.api_app.add_middleware(