Merge branch 'release/0.0.1'
This commit is contained in:
commit
5a321125de
@ -21,6 +21,8 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
|||||||
|
|
||||||
RUN curl -fsSL https://get.docker.com | bash
|
RUN curl -fsSL https://get.docker.com | bash
|
||||||
|
|
||||||
|
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN pip install --no-cache-dir -e .
|
RUN pip install --no-cache-dir -e .
|
||||||
|
@ -54,7 +54,8 @@ async def register_user(user_data: UserCreate, db: Session = Depends(get_db)):
|
|||||||
Raises:
|
Raises:
|
||||||
HTTPException: If there is an error in registration
|
HTTPException: If there is an error in registration
|
||||||
"""
|
"""
|
||||||
user, message = create_user(db, user_data, is_admin=False, auto_verify=False)
|
# TODO: remover o auto_verify temporariamente para teste
|
||||||
|
user, message = create_user(db, user_data, is_admin=False, auto_verify=True)
|
||||||
if not user:
|
if not user:
|
||||||
logger.error(f"Error registering user: {message}")
|
logger.error(f"Error registering user: {message}")
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||||
|
@ -37,7 +37,7 @@ router = APIRouter(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", response_model=Client, status_code=status.HTTP_201_CREATED)
|
@router.post("", response_model=Client, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_user(
|
async def create_user(
|
||||||
registration: ClientRegistration,
|
registration: ClientRegistration,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
|
@ -137,6 +137,9 @@ class LLMConfig(BaseModel):
|
|||||||
custom_mcp_servers: Optional[List[CustomMCPServerConfig]] = Field(
|
custom_mcp_servers: Optional[List[CustomMCPServerConfig]] = Field(
|
||||||
default=None, description="List of custom MCP servers with URL and headers"
|
default=None, description="List of custom MCP servers with URL and headers"
|
||||||
)
|
)
|
||||||
|
agent_tools: Optional[List[UUID]] = Field(
|
||||||
|
default=None, description="List of IDs of sub-agents"
|
||||||
|
)
|
||||||
sub_agents: Optional[List[UUID]] = Field(
|
sub_agents: Optional[List[UUID]] = Field(
|
||||||
default=None, description="List of IDs of sub-agents"
|
default=None, description="List of IDs of sub-agents"
|
||||||
)
|
)
|
||||||
|
@ -122,18 +122,6 @@ class AgentBase(BaseModel):
|
|||||||
def validate_api_key_id(cls, v, values):
|
def validate_api_key_id(cls, v, values):
|
||||||
return v
|
return v
|
||||||
|
|
||||||
# Código anterior (comentado temporariamente)
|
|
||||||
# # Se o tipo for llm, api_key_id é obrigatório
|
|
||||||
# if "type" in values and values["type"] == "llm" and not v:
|
|
||||||
# # Verifica se tem api_key no config (retrocompatibilidade)
|
|
||||||
# if "config" in values and values["config"] and "api_key" in values["config"]:
|
|
||||||
# # Tem api_key no config, então aceita
|
|
||||||
# return v
|
|
||||||
# raise ValueError(
|
|
||||||
# "api_key_id é obrigatório para agentes do tipo llm"
|
|
||||||
# )
|
|
||||||
# return v
|
|
||||||
|
|
||||||
@validator("config")
|
@validator("config")
|
||||||
def validate_config(cls, v, values):
|
def validate_config(cls, v, values):
|
||||||
if "type" in values and values["type"] == "a2a":
|
if "type" in values and values["type"] == "a2a":
|
||||||
|
@ -2,6 +2,7 @@ from typing import List, Optional, Tuple
|
|||||||
from google.adk.agents.llm_agent import LlmAgent
|
from google.adk.agents.llm_agent import LlmAgent
|
||||||
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, BaseAgent
|
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, BaseAgent
|
||||||
from google.adk.models.lite_llm import LiteLlm
|
from google.adk.models.lite_llm import LiteLlm
|
||||||
|
from google.adk.tools.agent_tool import AgentTool
|
||||||
from src.utils.logger import setup_logger
|
from src.utils.logger import setup_logger
|
||||||
from src.core.exceptions import AgentNotFoundError
|
from src.core.exceptions import AgentNotFoundError
|
||||||
from src.services.agent_service import get_agent
|
from src.services.agent_service import get_agent
|
||||||
@ -26,14 +27,25 @@ class AgentBuilder:
|
|||||||
self.custom_tool_builder = CustomToolBuilder()
|
self.custom_tool_builder = CustomToolBuilder()
|
||||||
self.mcp_service = MCPService()
|
self.mcp_service = MCPService()
|
||||||
|
|
||||||
|
async def _agent_tools_builder(self, agent) -> List[AgentTool]:
|
||||||
|
"""Build the tools for an agent."""
|
||||||
|
agent_tools_ids = agent.config.get("agent_tools")
|
||||||
|
agent_tools = []
|
||||||
|
if agent_tools_ids and isinstance(agent_tools_ids, list):
|
||||||
|
for agent_tool_id in agent_tools_ids:
|
||||||
|
sub_agent = get_agent(self.db, agent_tool_id)
|
||||||
|
llm_agent, _ = await self.build_llm_agent(sub_agent)
|
||||||
|
if llm_agent:
|
||||||
|
agent_tools.append(AgentTool(agent=llm_agent))
|
||||||
|
return agent_tools
|
||||||
|
|
||||||
async def _create_llm_agent(
|
async def _create_llm_agent(
|
||||||
self, agent
|
self, agent
|
||||||
) -> Tuple[LlmAgent, Optional[AsyncExitStack]]:
|
) -> Tuple[LlmAgent, Optional[AsyncExitStack]]:
|
||||||
"""Create an LLM agent from the agent data."""
|
"""Create an LLM agent from the agent data."""
|
||||||
# Get custom tools from the configuration
|
# Get custom tools from the configuration
|
||||||
custom_tools = []
|
custom_tools = []
|
||||||
if agent.config.get("tools"):
|
custom_tools = self.custom_tool_builder.build_tools(agent.config)
|
||||||
custom_tools = self.custom_tool_builder.build_tools(agent.config["tools"])
|
|
||||||
|
|
||||||
# Get MCP tools from the configuration
|
# Get MCP tools from the configuration
|
||||||
mcp_tools = []
|
mcp_tools = []
|
||||||
@ -43,8 +55,11 @@ class AgentBuilder:
|
|||||||
agent.config, self.db
|
agent.config, self.db
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get agent tools
|
||||||
|
agent_tools = await self._agent_tools_builder(agent)
|
||||||
|
|
||||||
# Combine all tools
|
# Combine all tools
|
||||||
all_tools = custom_tools + mcp_tools
|
all_tools = custom_tools + mcp_tools + agent_tools
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
current_datetime = now.strftime("%d/%m/%Y %H:%M")
|
current_datetime = now.strftime("%d/%m/%Y %H:%M")
|
||||||
|
@ -224,6 +224,9 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
|||||||
if "custom_tools" in config:
|
if "custom_tools" in config:
|
||||||
processed_config["custom_tools"] = config["custom_tools"]
|
processed_config["custom_tools"] = config["custom_tools"]
|
||||||
|
|
||||||
|
if "agent_tools" in config:
|
||||||
|
processed_config["agent_tools"] = config["agent_tools"]
|
||||||
|
|
||||||
if "sub_agents" in config:
|
if "sub_agents" in config:
|
||||||
processed_config["sub_agents"] = config["sub_agents"]
|
processed_config["sub_agents"] = config["sub_agents"]
|
||||||
|
|
||||||
@ -236,6 +239,7 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
|||||||
"tools",
|
"tools",
|
||||||
"custom_tools",
|
"custom_tools",
|
||||||
"sub_agents",
|
"sub_agents",
|
||||||
|
"agent_tools",
|
||||||
"custom_mcp_servers",
|
"custom_mcp_servers",
|
||||||
"mcp_servers",
|
"mcp_servers",
|
||||||
]:
|
]:
|
||||||
@ -303,6 +307,12 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
|||||||
str(agent_id) for agent_id in config["sub_agents"]
|
str(agent_id) for agent_id in config["sub_agents"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Process agent tools
|
||||||
|
if "agent_tools" in config and config["agent_tools"] is not None:
|
||||||
|
processed_config["agent_tools"] = [
|
||||||
|
str(agent_id) for agent_id in config["agent_tools"]
|
||||||
|
]
|
||||||
|
|
||||||
# Process tools
|
# Process tools
|
||||||
if "tools" in config and config["tools"] is not None:
|
if "tools" in config and config["tools"] is not None:
|
||||||
processed_tools = []
|
processed_tools = []
|
||||||
@ -484,6 +494,9 @@ async def update_agent(
|
|||||||
if "sub_agents" in config:
|
if "sub_agents" in config:
|
||||||
processed_config["sub_agents"] = config["sub_agents"]
|
processed_config["sub_agents"] = config["sub_agents"]
|
||||||
|
|
||||||
|
if "agent_tools" in config:
|
||||||
|
processed_config["agent_tools"] = config["agent_tools"]
|
||||||
|
|
||||||
if "custom_mcp_servers" in config:
|
if "custom_mcp_servers" in config:
|
||||||
processed_config["custom_mcp_servers"] = config["custom_mcp_servers"]
|
processed_config["custom_mcp_servers"] = config["custom_mcp_servers"]
|
||||||
|
|
||||||
@ -493,6 +506,7 @@ async def update_agent(
|
|||||||
"tools",
|
"tools",
|
||||||
"custom_tools",
|
"custom_tools",
|
||||||
"sub_agents",
|
"sub_agents",
|
||||||
|
"agent_tools",
|
||||||
"custom_mcp_servers",
|
"custom_mcp_servers",
|
||||||
"mcp_servers",
|
"mcp_servers",
|
||||||
]:
|
]:
|
||||||
@ -563,6 +577,12 @@ async def update_agent(
|
|||||||
str(agent_id) for agent_id in config["sub_agents"]
|
str(agent_id) for agent_id in config["sub_agents"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Process agent tools
|
||||||
|
if "agent_tools" in config and config["agent_tools"] is not None:
|
||||||
|
processed_config["agent_tools"] = [
|
||||||
|
str(agent_id) for agent_id in config["agent_tools"]
|
||||||
|
]
|
||||||
|
|
||||||
# Process tools
|
# Process tools
|
||||||
if "tools" in config and config["tools"] is not None:
|
if "tools" in config and config["tools"] is not None:
|
||||||
processed_tools = []
|
processed_tools = []
|
||||||
|
@ -18,10 +18,14 @@ class CustomToolBuilder:
|
|||||||
endpoint = tool_config["endpoint"]
|
endpoint = tool_config["endpoint"]
|
||||||
method = tool_config["method"]
|
method = tool_config["method"]
|
||||||
headers = tool_config.get("headers", {})
|
headers = tool_config.get("headers", {})
|
||||||
parameters = tool_config.get("parameters", {})
|
parameters = tool_config.get("parameters", {}) or {}
|
||||||
values = tool_config.get("values", {})
|
values = tool_config.get("values", {})
|
||||||
error_handling = tool_config.get("error_handling", {})
|
error_handling = tool_config.get("error_handling", {})
|
||||||
|
|
||||||
|
path_params = parameters.get("path_params") or {}
|
||||||
|
query_params = parameters.get("query_params") or {}
|
||||||
|
body_params = parameters.get("body_params") or {}
|
||||||
|
|
||||||
def http_tool(**kwargs):
|
def http_tool(**kwargs):
|
||||||
try:
|
try:
|
||||||
# Combines default values with provided values
|
# Combines default values with provided values
|
||||||
@ -35,32 +39,30 @@ class CustomToolBuilder:
|
|||||||
|
|
||||||
# Processes path parameters
|
# Processes path parameters
|
||||||
url = endpoint
|
url = endpoint
|
||||||
for param, value in parameters.get("path_params", {}).items():
|
for param, value in path_params.items():
|
||||||
if param in all_values:
|
if param in all_values:
|
||||||
url = url.replace(f"{{{param}}}", str(all_values[param]))
|
url = url.replace(f"{{{param}}}", str(all_values[param]))
|
||||||
|
|
||||||
# Process query parameters
|
# Process query parameters
|
||||||
query_params = {}
|
query_params_dict = {}
|
||||||
for param, value in parameters.get("query_params", {}).items():
|
for param, value in query_params.items():
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
# If the value is a list, join with comma
|
# If the value is a list, join with comma
|
||||||
query_params[param] = ",".join(value)
|
query_params_dict[param] = ",".join(value)
|
||||||
elif param in all_values:
|
elif param in all_values:
|
||||||
# If the parameter is in the values, use the provided value
|
# If the parameter is in the values, use the provided value
|
||||||
query_params[param] = all_values[param]
|
query_params_dict[param] = all_values[param]
|
||||||
else:
|
else:
|
||||||
# Otherwise, use the default value from the configuration
|
# Otherwise, use the default value from the configuration
|
||||||
query_params[param] = value
|
query_params_dict[param] = value
|
||||||
|
|
||||||
# Adds default values to query params if they are not present
|
# Adds default values to query params if they are not present
|
||||||
for param, value in values.items():
|
for param, value in values.items():
|
||||||
if param not in query_params and param not in parameters.get(
|
if param not in query_params_dict and param not in path_params:
|
||||||
"path_params", {}
|
query_params_dict[param] = value
|
||||||
):
|
|
||||||
query_params[param] = value
|
|
||||||
|
|
||||||
body_data = {}
|
body_data = {}
|
||||||
for param, param_config in parameters.get("body_params", {}).items():
|
for param, param_config in body_params.items():
|
||||||
if param in all_values:
|
if param in all_values:
|
||||||
body_data[param] = all_values[param]
|
body_data[param] = all_values[param]
|
||||||
|
|
||||||
@ -68,8 +70,8 @@ class CustomToolBuilder:
|
|||||||
for param, value in values.items():
|
for param, value in values.items():
|
||||||
if (
|
if (
|
||||||
param not in body_data
|
param not in body_data
|
||||||
and param not in query_params
|
and param not in query_params_dict
|
||||||
and param not in parameters.get("path_params", {})
|
and param not in path_params
|
||||||
):
|
):
|
||||||
body_data[param] = value
|
body_data[param] = value
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ class CustomToolBuilder:
|
|||||||
method=method,
|
method=method,
|
||||||
url=url,
|
url=url,
|
||||||
headers=processed_headers,
|
headers=processed_headers,
|
||||||
params=query_params,
|
params=query_params_dict,
|
||||||
json=body_data if body_data else None,
|
json=body_data if body_data else None,
|
||||||
timeout=error_handling.get("timeout", 30),
|
timeout=error_handling.get("timeout", 30),
|
||||||
)
|
)
|
||||||
@ -104,18 +106,18 @@ class CustomToolBuilder:
|
|||||||
param_docs = []
|
param_docs = []
|
||||||
|
|
||||||
# Adds path parameters
|
# Adds path parameters
|
||||||
for param, value in parameters.get("path_params", {}).items():
|
for param, value in path_params.items():
|
||||||
param_docs.append(f"{param}: {value}")
|
param_docs.append(f"{param}: {value}")
|
||||||
|
|
||||||
# Adds query parameters
|
# Adds query parameters
|
||||||
for param, value in parameters.get("query_params", {}).items():
|
for param, value in query_params.items():
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
param_docs.append(f"{param}: List[{', '.join(value)}]")
|
param_docs.append(f"{param}: List[{', '.join(value)}]")
|
||||||
else:
|
else:
|
||||||
param_docs.append(f"{param}: {value}")
|
param_docs.append(f"{param}: {value}")
|
||||||
|
|
||||||
# Adds body parameters
|
# Adds body parameters
|
||||||
for param, param_config in parameters.get("body_params", {}).items():
|
for param, param_config in body_params.items():
|
||||||
required = "Required" if param_config.get("required", False) else "Optional"
|
required = "Required" if param_config.get("required", False) else "Optional"
|
||||||
param_docs.append(
|
param_docs.append(
|
||||||
f"{param} ({param_config['type']}, {required}): {param_config['description']}"
|
f"{param} ({param_config['type']}, {required}): {param_config['description']}"
|
||||||
@ -143,11 +145,24 @@ class CustomToolBuilder:
|
|||||||
return FunctionTool(func=http_tool)
|
return FunctionTool(func=http_tool)
|
||||||
|
|
||||||
def build_tools(self, tools_config: Dict[str, Any]) -> List[FunctionTool]:
|
def build_tools(self, tools_config: Dict[str, Any]) -> List[FunctionTool]:
|
||||||
"""Builds a list of tools based on the provided configuration."""
|
"""Builds a list of tools based on the provided configuration. Accepts both 'tools' and 'custom_tools' (with http_tools)."""
|
||||||
self.tools = []
|
self.tools = []
|
||||||
|
|
||||||
# Processes HTTP tools
|
http_tools = []
|
||||||
for http_tool_config in tools_config.get("http_tools", []):
|
if tools_config.get("http_tools"):
|
||||||
|
http_tools = tools_config.get("http_tools", [])
|
||||||
|
elif tools_config.get("custom_tools") and tools_config["custom_tools"].get(
|
||||||
|
"http_tools"
|
||||||
|
):
|
||||||
|
http_tools = tools_config["custom_tools"].get("http_tools", [])
|
||||||
|
elif (
|
||||||
|
tools_config.get("tools")
|
||||||
|
and isinstance(tools_config["tools"], dict)
|
||||||
|
and tools_config["tools"].get("http_tools")
|
||||||
|
):
|
||||||
|
http_tools = tools_config["tools"].get("http_tools", [])
|
||||||
|
|
||||||
|
for http_tool_config in http_tools:
|
||||||
self.tools.append(self._create_http_tool(http_tool_config))
|
self.tools.append(self._create_http_tool(http_tool_config))
|
||||||
|
|
||||||
return self.tools
|
return self.tools
|
||||||
|
@ -79,6 +79,9 @@ class MCPService:
|
|||||||
self, tools: List[Any], agent_tools: List[str]
|
self, tools: List[Any], agent_tools: List[str]
|
||||||
) -> List[Any]:
|
) -> List[Any]:
|
||||||
"""Filters tools compatible with the agent."""
|
"""Filters tools compatible with the agent."""
|
||||||
|
if not agent_tools or len(agent_tools) == 0:
|
||||||
|
return tools
|
||||||
|
|
||||||
filtered_tools = []
|
filtered_tools = []
|
||||||
for tool in tools:
|
for tool in tools:
|
||||||
logger.info(f"Tool: {tool.name}")
|
logger.info(f"Tool: {tool.name}")
|
||||||
|
@ -304,10 +304,47 @@ class WorkflowAgent(BaseAgent):
|
|||||||
"session_id": session_id,
|
"session_id": session_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Função para message-node
|
||||||
|
async def message_node_function(
|
||||||
|
state: State, node_id: str, node_data: Dict[str, Any]
|
||||||
|
) -> AsyncGenerator[State, None]:
|
||||||
|
message_data = node_data.get("message", {})
|
||||||
|
message_type = message_data.get("type", "text")
|
||||||
|
message_content = message_data.get("content", "")
|
||||||
|
|
||||||
|
print(f"\n💬 MESSAGE-NODE: {message_content}")
|
||||||
|
|
||||||
|
content = state.get("content", [])
|
||||||
|
session_id = state.get("session_id", "")
|
||||||
|
conversation_history = state.get("conversation_history", [])
|
||||||
|
|
||||||
|
# Adiciona a mensagem como um novo Event do tipo agent
|
||||||
|
new_event = Event(
|
||||||
|
author="agent",
|
||||||
|
content=Content(parts=[Part(text=message_content)]),
|
||||||
|
)
|
||||||
|
content = content + [new_event]
|
||||||
|
|
||||||
|
node_outputs = state.get("node_outputs", {})
|
||||||
|
node_outputs[node_id] = {
|
||||||
|
"message_type": message_type,
|
||||||
|
"message_content": message_content,
|
||||||
|
}
|
||||||
|
|
||||||
|
yield {
|
||||||
|
"content": content,
|
||||||
|
"status": "message_added",
|
||||||
|
"node_outputs": node_outputs,
|
||||||
|
"cycle_count": state.get("cycle_count", 0),
|
||||||
|
"conversation_history": conversation_history,
|
||||||
|
"session_id": session_id,
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"start-node": start_node_function,
|
"start-node": start_node_function,
|
||||||
"agent-node": agent_node_function,
|
"agent-node": agent_node_function,
|
||||||
"condition-node": condition_node_function,
|
"condition-node": condition_node_function,
|
||||||
|
"message-node": message_node_function,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _evaluate_condition(self, condition: Dict[str, Any], state: State) -> bool:
|
def _evaluate_condition(self, condition: Dict[str, Any], state: State) -> bool:
|
||||||
@ -708,18 +745,23 @@ class WorkflowAgent(BaseAgent):
|
|||||||
graph = await self._create_graph(ctx, self.flow_json)
|
graph = await self._create_graph(ctx, self.flow_json)
|
||||||
|
|
||||||
# 4. Prepare the initial state
|
# 4. Prepare the initial state
|
||||||
initial_state = State(
|
user_event = Event(
|
||||||
content=[
|
|
||||||
Event(
|
|
||||||
author="user",
|
author="user",
|
||||||
content=Content(parts=[Part(text=user_message)]),
|
content=Content(parts=[Part(text=user_message)]),
|
||||||
)
|
)
|
||||||
],
|
|
||||||
|
# Se o histórico estiver vazio, adiciona a mensagem do usuário
|
||||||
|
conversation_history = ctx.session.events or []
|
||||||
|
if not conversation_history or (len(conversation_history) == 0):
|
||||||
|
conversation_history = [user_event]
|
||||||
|
|
||||||
|
initial_state = State(
|
||||||
|
content=[user_event],
|
||||||
status="started",
|
status="started",
|
||||||
session_id=session_id,
|
session_id=session_id,
|
||||||
cycle_count=0,
|
cycle_count=0,
|
||||||
node_outputs={},
|
node_outputs={},
|
||||||
conversation_history=ctx.session.events,
|
conversation_history=conversation_history,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. Execute the graph
|
# 5. Execute the graph
|
||||||
|
Loading…
Reference in New Issue
Block a user