evo-ai/src/schemas/schemas.py
2025-05-13 07:30:52 -03:00

296 lines
10 KiB
Python

"""
┌──────────────────────────────────────────────────────────────────────────────┐
│ @author: Davidson Gomes │
│ @file: run_seeders.py │
│ Developed by: Davidson Gomes │
│ Creation date: May 13, 2025 │
│ Contact: contato@evolution-api.com │
├──────────────────────────────────────────────────────────────────────────────┤
│ @copyright © Evolution API 2025. All rights reserved. │
│ Licensed under the Apache License, Version 2.0 │
│ │
│ You may not use this file except in compliance with the License. │
│ You may obtain a copy of the License at │
│ │
│ http://www.apache.org/licenses/LICENSE-2.0 │
│ │
│ Unless required by applicable law or agreed to in writing, software │
│ distributed under the License is distributed on an "AS IS" BASIS, │
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
│ See the License for the specific language governing permissions and │
│ limitations under the License. │
├──────────────────────────────────────────────────────────────────────────────┤
│ @important │
│ For any future changes to the code in this file, it is recommended to │
│ include, together with the modification, the information of the developer │
│ who changed it and the date of modification. │
└──────────────────────────────────────────────────────────────────────────────┘
"""
from pydantic import BaseModel, Field, validator, UUID4, ConfigDict
from typing import Optional, Dict, Any, List
from datetime import datetime
from uuid import UUID
import uuid
import re
from src.schemas.agent_config import LLMConfig
class ClientBase(BaseModel):
name: str
email: Optional[str] = None
@validator("email")
def validate_email(cls, v):
if v is None:
return v
email_regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
if not re.match(email_regex, v):
raise ValueError("Invalid email format")
return v
class ClientCreate(ClientBase):
pass
class Client(ClientBase):
id: UUID
created_at: datetime
class Config:
from_attributes = True
class ApiKeyBase(BaseModel):
name: str
provider: str
class ApiKeyCreate(ApiKeyBase):
client_id: UUID4
key_value: str
class ApiKeyUpdate(BaseModel):
name: Optional[str] = None
provider: Optional[str] = None
key_value: Optional[str] = None
is_active: Optional[bool] = None
class ApiKey(ApiKeyBase):
id: UUID4
client_id: UUID4
created_at: datetime
updated_at: Optional[datetime] = None
is_active: bool
model_config = ConfigDict(from_attributes=True)
class AgentBase(BaseModel):
name: Optional[str] = Field(
None, description="Agent name (no spaces or special characters)"
)
description: Optional[str] = Field(None, description="Agent description")
type: str = Field(
..., description="Agent type (llm, sequential, parallel, loop, a2a, workflow)"
)
model: Optional[str] = Field(
None, description="Agent model (required only for llm type)"
)
api_key_id: Optional[UUID4] = Field(
None, description="Reference to a stored API Key ID"
)
instruction: Optional[str] = None
agent_card_url: Optional[str] = Field(
None, description="Agent card URL (required for a2a type)"
)
folder_id: Optional[UUID4] = Field(
None, description="ID of the folder this agent belongs to"
)
config: Any = Field(None, description="Agent configuration based on type")
@validator("name")
def validate_name(cls, v, values):
if values.get("type") == "a2a":
return v
if not v:
raise ValueError("Name is required for non-a2a agent types")
if not re.match(r"^[a-zA-Z0-9_-]+$", v):
raise ValueError("Agent name cannot contain spaces or special characters")
return v
@validator("type")
def validate_type(cls, v):
if v not in ["llm", "sequential", "parallel", "loop", "a2a", "workflow"]:
raise ValueError(
"Invalid agent type. Must be: llm, sequential, parallel, loop, a2a or workflow"
)
return v
@validator("agent_card_url")
def validate_agent_card_url(cls, v, values):
if "type" in values and values["type"] == "a2a":
if not v:
raise ValueError("agent_card_url is required for a2a type agents")
if not v.endswith("/.well-known/agent.json"):
raise ValueError("agent_card_url must end with /.well-known/agent.json")
return v
@validator("model")
def validate_model(cls, v, values):
if "type" in values and values["type"] == "llm" and not v:
raise ValueError("Model is required for llm type agents")
return v
@validator("api_key_id")
def validate_api_key_id(cls, v, values):
return v
@validator("config")
def validate_config(cls, v, values):
if "type" in values and values["type"] == "a2a":
return v or {}
if "type" not in values:
return v
# For workflow agents, we do not perform any validation
if "type" in values and values["type"] == "workflow":
return v
if not v and values.get("type") != "a2a":
raise ValueError(
f"Configuration is required for {values.get('type')} agent type"
)
if values["type"] == "llm":
if isinstance(v, dict):
try:
# Convert the dictionary to LLMConfig
v = LLMConfig(**v)
except Exception as e:
raise ValueError(f"Invalid LLM configuration for agent: {str(e)}")
elif not isinstance(v, LLMConfig):
raise ValueError("Invalid LLM configuration for agent")
elif values["type"] in ["sequential", "parallel", "loop"]:
if not isinstance(v, dict):
raise ValueError(f'Invalid configuration for agent {values["type"]}')
if "sub_agents" not in v:
raise ValueError(f'Agent {values["type"]} must have sub_agents')
if not isinstance(v["sub_agents"], list):
raise ValueError("sub_agents must be a list")
if not v["sub_agents"]:
raise ValueError(
f'Agent {values["type"]} must have at least one sub-agent'
)
return v
class AgentCreate(AgentBase):
client_id: UUID
class Agent(AgentBase):
id: UUID
client_id: UUID
created_at: datetime
updated_at: Optional[datetime] = None
agent_card_url: Optional[str] = None
folder_id: Optional[UUID4] = None
class Config:
from_attributes = True
@validator("agent_card_url", pre=True)
def set_agent_card_url(cls, v, values):
if v:
return v
if "id" in values:
from os import getenv
return f"{getenv('API_URL', '')}/api/v1/a2a/{values['id']}/.well-known/agent.json"
return v
class ToolConfig(BaseModel):
id: str
name: str
description: str
tags: List[str] = Field(default_factory=list)
examples: List[str] = Field(default_factory=list)
inputModes: List[str] = Field(default_factory=list)
outputModes: List[str] = Field(default_factory=list)
class MCPServerBase(BaseModel):
name: str
description: Optional[str] = None
config_type: str = Field(default="studio")
config_json: Dict[str, Any] = Field(default_factory=dict)
environments: Dict[str, Any] = Field(default_factory=dict)
tools: List[ToolConfig] = Field(default_factory=list)
type: str = Field(default="official")
class MCPServerCreate(MCPServerBase):
pass
class MCPServer(MCPServerBase):
id: uuid.UUID
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class ToolBase(BaseModel):
name: str
description: Optional[str] = None
config_json: Dict[str, Any] = Field(default_factory=dict)
environments: Dict[str, Any] = Field(default_factory=dict)
class ToolCreate(ToolBase):
pass
class Tool(ToolBase):
id: uuid.UUID
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class AgentFolderBase(BaseModel):
name: str
description: Optional[str] = None
class AgentFolderCreate(AgentFolderBase):
client_id: UUID4
class AgentFolderUpdate(AgentFolderBase):
pass
class AgentFolder(AgentFolderBase):
id: UUID4
client_id: UUID4
created_at: datetime
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)