chore(cleanup): remove contact-related files and update JWT expiration time settings
This commit is contained in:
parent
8979251541
commit
0e3c331a72
@ -23,7 +23,6 @@ src/
|
||||
│ ├── auth_routes.py # Authentication routes (login, registration)
|
||||
│ ├── chat_routes.py # Routes for chat interactions with agents
|
||||
│ ├── client_routes.py # Routes to manage clients
|
||||
│ ├── contact_routes.py # Routes to manage contacts
|
||||
│ ├── mcp_server_routes.py # Routes to manage MCP servers
|
||||
│ ├── session_routes.py # Routes to manage chat sessions
|
||||
│ └── tool_routes.py # Routes to manage tools for agents
|
||||
@ -46,7 +45,6 @@ src/
|
||||
│ ├── auth_service.py # JWT authentication logic
|
||||
│ ├── audit_service.py # Audit logs logic
|
||||
│ ├── client_service.py # Business logic for clients
|
||||
│ ├── contact_service.py # Business logic for contacts
|
||||
│ ├── email_service.py # Email sending service
|
||||
│ ├── mcp_server_service.py # Business logic for MCP servers
|
||||
│ ├── session_service.py # Business logic for chat sessions
|
||||
|
10
.env.example
10
.env.example
@ -28,8 +28,8 @@ TOOLS_CACHE_TTL=3600
|
||||
# JWT settings
|
||||
JWT_SECRET_KEY="your-jwt-secret-key"
|
||||
JWT_ALGORITHM="HS256"
|
||||
# In minutes
|
||||
JWT_EXPIRATION_TIME=30
|
||||
# In seconds
|
||||
JWT_EXPIRATION_TIME=3600
|
||||
|
||||
# SendGrid
|
||||
SENDGRID_API_KEY="your-sendgrid-api-key"
|
||||
@ -47,9 +47,3 @@ ADMIN_INITIAL_PASSWORD="strongpassword123"
|
||||
DEMO_EMAIL="demo@example.com"
|
||||
DEMO_PASSWORD="demo123"
|
||||
DEMO_CLIENT_NAME="Demo Client"
|
||||
|
||||
# A2A settings
|
||||
A2A_TASK_TTL=3600
|
||||
A2A_HISTORY_TTL=86400
|
||||
A2A_PUSH_NOTIFICATION_TTL=3600
|
||||
A2A_SSE_CLIENT_TTL=300
|
||||
|
8
Makefile
8
Makefile
@ -1,4 +1,4 @@
|
||||
.PHONY: migrate init revision upgrade downgrade run seed-admin seed-client seed-agents seed-mcp-servers seed-tools seed-contacts seed-all docker-build docker-up docker-down docker-logs lint format install install-dev venv
|
||||
.PHONY: migrate init revision upgrade downgrade run seed-admin seed-client seed-mcp-servers seed-tools seed-all docker-build docker-up docker-down docker-logs lint format install install-dev venv
|
||||
|
||||
# Alembic commands
|
||||
init:
|
||||
@ -43,18 +43,12 @@ seed-admin:
|
||||
seed-client:
|
||||
python -m scripts.seeders.client_seeder
|
||||
|
||||
seed-agents:
|
||||
python -m scripts.seeders.agent_seeder
|
||||
|
||||
seed-mcp-servers:
|
||||
python -m scripts.seeders.mcp_server_seeder
|
||||
|
||||
seed-tools:
|
||||
python -m scripts.seeders.tool_seeder
|
||||
|
||||
seed-contacts:
|
||||
python -m scripts.seeders.contact_seeder
|
||||
|
||||
seed-all:
|
||||
python -m scripts.run_seeders
|
||||
|
||||
|
10
README.md
10
README.md
@ -8,7 +8,7 @@ The Evo AI platform allows:
|
||||
|
||||
- Creation and management of AI agents
|
||||
- Integration with different language models
|
||||
- Client and contact management
|
||||
- Client management
|
||||
- MCP server configuration
|
||||
- Custom tools management
|
||||
- JWT authentication with email verification
|
||||
@ -463,16 +463,12 @@ REDIS_PASSWORD="your-redis-password"
|
||||
# JWT settings
|
||||
JWT_SECRET_KEY="your-jwt-secret-key"
|
||||
JWT_ALGORITHM="HS256"
|
||||
JWT_EXPIRATION_TIME=30 # In minutes
|
||||
JWT_EXPIRATION_TIME=30 # In seconds
|
||||
|
||||
# SendGrid for emails
|
||||
SENDGRID_API_KEY="your-sendgrid-api-key"
|
||||
EMAIL_FROM="noreply@yourdomain.com"
|
||||
APP_URL="https://yourdomain.com"
|
||||
|
||||
# A2A settings
|
||||
A2A_TASK_TTL=3600
|
||||
A2A_HISTORY_TTL=86400
|
||||
```
|
||||
|
||||
### Project Dependencies
|
||||
@ -645,10 +641,8 @@ make alembic-reset # Reset database
|
||||
# Seeders
|
||||
make seed-admin # Create default admin
|
||||
make seed-client # Create default client
|
||||
make seed-agents # Create example agents
|
||||
make seed-mcp-servers # Create example MCP servers
|
||||
make seed-tools # Create example tools
|
||||
make seed-contacts # Create example contacts
|
||||
make seed-all # Run all seeders
|
||||
|
||||
# Code verification
|
||||
|
@ -19,10 +19,6 @@ services:
|
||||
REDIS_SSL: "false"
|
||||
REDIS_KEY_PREFIX: "a2a:"
|
||||
REDIS_TTL: 3600
|
||||
A2A_TASK_TTL: 3600
|
||||
A2A_HISTORY_TTL: 86400
|
||||
A2A_PUSH_NOTIFICATION_TTL: 3600
|
||||
A2A_SSE_CLIENT_TTL: 300
|
||||
JWT_SECRET_KEY: ${JWT_SECRET_KEY}
|
||||
SENDGRID_API_KEY: ${SENDGRID_API_KEY}
|
||||
EMAIL_FROM: ${EMAIL_FROM}
|
||||
|
@ -10,17 +10,18 @@ import argparse
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Import seeders
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from scripts.seeders.admin_seeder import create_admin_user
|
||||
from scripts.seeders.client_seeder import create_demo_client_and_user
|
||||
from scripts.seeders.agent_seeder import create_demo_agents
|
||||
from scripts.seeders.mcp_server_seeder import create_mcp_servers
|
||||
from scripts.seeders.tool_seeder import create_tools
|
||||
from scripts.seeders.contact_seeder import create_demo_contacts
|
||||
|
||||
|
||||
def setup_environment():
|
||||
"""Configure the environment for seeders"""
|
||||
@ -31,11 +32,14 @@ def setup_environment():
|
||||
missing_vars = [var for var in required_vars if not os.getenv(var)]
|
||||
|
||||
if missing_vars:
|
||||
logger.error(f"Required environment variables not defined: {', '.join(missing_vars)}")
|
||||
logger.error(
|
||||
f"Required environment variables not defined: {', '.join(missing_vars)}"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def run_seeders(seeders):
|
||||
"""
|
||||
Run the specified seeders
|
||||
@ -49,14 +53,12 @@ def run_seeders(seeders):
|
||||
all_seeders = {
|
||||
"admin": create_admin_user,
|
||||
"client": create_demo_client_and_user,
|
||||
"agents": create_demo_agents,
|
||||
"mcp_servers": create_mcp_servers,
|
||||
"tools": create_tools,
|
||||
"contacts": create_demo_contacts
|
||||
}
|
||||
|
||||
# Define the correct execution order (dependencies)
|
||||
seeder_order = ["admin", "client", "mcp_servers", "tools", "agents", "contacts"]
|
||||
seeder_order = ["admin", "client", "mcp_servers", "tools"]
|
||||
|
||||
# If no seeder is specified, run all
|
||||
if not seeders:
|
||||
@ -88,10 +90,15 @@ def run_seeders(seeders):
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function"""
|
||||
parser = argparse.ArgumentParser(description='Run seeders to populate the database')
|
||||
parser.add_argument('--seeders', nargs='+', help='Seeders to run (admin, client, agents, mcp_servers, tools, contacts)')
|
||||
parser = argparse.ArgumentParser(description="Run seeders to populate the database")
|
||||
parser.add_argument(
|
||||
"--seeders",
|
||||
nargs="+",
|
||||
help="Seeders to run (admin, client, mcp_servers, tools)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure environment
|
||||
@ -109,5 +116,6 @@ def main():
|
||||
logger.error("There were errors running the seeders")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,171 +0,0 @@
|
||||
"""
|
||||
Script to create example agents for the demo client:
|
||||
- Agent Support: configured to answer general questions
|
||||
- Agent Sales: configured to answer about products
|
||||
- Agent FAQ: configured to answer frequently asked questions
|
||||
Each agent with pre-defined instructions and configurations
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import uuid
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from dotenv import load_dotenv
|
||||
from src.models.models import Agent, Client, User
|
||||
|
||||
# Configurar logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_demo_agents():
|
||||
"""Create example agents for the demo client"""
|
||||
try:
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Get database settings
|
||||
db_url = os.getenv("POSTGRES_CONNECTION_STRING")
|
||||
if not db_url:
|
||||
logger.error("Environment variable POSTGRES_CONNECTION_STRING not defined")
|
||||
return False
|
||||
|
||||
# Connect to the database
|
||||
engine = create_engine(db_url)
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
try:
|
||||
# Obter o cliente demo pelo email do usuário
|
||||
demo_email = os.getenv("DEMO_EMAIL", "demo@exemplo.com")
|
||||
demo_user = session.query(User).filter(User.email == demo_email).first()
|
||||
|
||||
if not demo_user or not demo_user.client_id:
|
||||
logger.error(
|
||||
f"Demo user not found or not associated with a client: {demo_email}"
|
||||
)
|
||||
return False
|
||||
|
||||
client_id = demo_user.client_id
|
||||
|
||||
# Verificar se já existem agentes para este cliente
|
||||
existing_agents = (
|
||||
session.query(Agent).filter(Agent.client_id == client_id).all()
|
||||
)
|
||||
if existing_agents:
|
||||
logger.info(
|
||||
f"There are already {len(existing_agents)} agents for the client {client_id}"
|
||||
)
|
||||
return True
|
||||
|
||||
# Example agent definitions
|
||||
agents = [
|
||||
{
|
||||
"name": "Support_Agent",
|
||||
"description": "Agent for general support and basic questions",
|
||||
"type": "llm",
|
||||
"model": "gpt-4.1-nano",
|
||||
"api_key": "your-api-key-here",
|
||||
"instruction": """
|
||||
You are a customer support agent.
|
||||
Be friendly, objective and efficient. Answer customer questions
|
||||
in a clear and concise manner. If you don't know the answer,
|
||||
inform that you will consult a specialist and return soon.
|
||||
""",
|
||||
"config": {
|
||||
"api_key": uuid.uuid4(),
|
||||
"tools": [],
|
||||
"mcp_servers": [],
|
||||
"custom_tools": [],
|
||||
"sub_agents": [],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Sales_Products",
|
||||
"description": "Specialized agent in sales and information about products",
|
||||
"type": "llm",
|
||||
"model": "gpt-4.1-nano",
|
||||
"api_key": "your-api-key-here",
|
||||
"instruction": """
|
||||
You are a sales specialist.
|
||||
Your goal is to provide detailed information about products,
|
||||
compare different options, highlight benefits and competitive advantages.
|
||||
Use a persuasive but honest language, and always seek to understand
|
||||
the customer's needs before recommending a product.
|
||||
""",
|
||||
"config": {
|
||||
"api_key": uuid.uuid4(),
|
||||
"tools": [],
|
||||
"mcp_servers": [],
|
||||
"custom_tools": [],
|
||||
"sub_agents": [],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "FAQ_Bot",
|
||||
"description": "Agent for answering frequently asked questions",
|
||||
"type": "llm",
|
||||
"model": "gpt-4.1-nano",
|
||||
"api_key": "your-api-key-here",
|
||||
"instruction": """
|
||||
You are a specialized agent for answering frequently asked questions.
|
||||
Your answers should be direct, objective and based on the information
|
||||
of the company. Use a simple and accessible language. If the question
|
||||
is not related to the available FAQs, redirect the client to the
|
||||
appropriate support channel.
|
||||
""",
|
||||
"config": {
|
||||
"api_key": uuid.uuid4(),
|
||||
"tools": [],
|
||||
"mcp_servers": [],
|
||||
"custom_tools": [],
|
||||
"sub_agents": [],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# Create the agents
|
||||
for agent_data in agents:
|
||||
# Create the agent
|
||||
agent = Agent(
|
||||
client_id=client_id,
|
||||
name=agent_data["name"],
|
||||
description=agent_data["description"],
|
||||
type=agent_data["type"],
|
||||
model=agent_data["model"],
|
||||
api_key=agent_data["api_key"],
|
||||
instruction=agent_data["instruction"].strip(),
|
||||
config=agent_data["config"],
|
||||
)
|
||||
|
||||
session.add(agent)
|
||||
logger.info(
|
||||
f"Agent '{agent_data['name']}' created for the client {client_id}"
|
||||
)
|
||||
|
||||
session.commit()
|
||||
logger.info(
|
||||
f"All example agents were created successfully for the client {client_id}"
|
||||
)
|
||||
return True
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
session.rollback()
|
||||
logger.error(f"Database error when creating example agents: {str(e)}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error when creating example agents: {str(e)}")
|
||||
return False
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = create_demo_agents()
|
||||
sys.exit(0 if success else 1)
|
@ -20,9 +20,12 @@ from src.models.models import User, Client
|
||||
from src.utils.security import get_password_hash
|
||||
|
||||
# Configurar logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_demo_client_and_user():
|
||||
"""Create a demo client and user in the system"""
|
||||
try:
|
||||
@ -55,7 +58,7 @@ def create_demo_client_and_user():
|
||||
return True
|
||||
|
||||
# Create demo client
|
||||
demo_client = Client(name=demo_client_name)
|
||||
demo_client = Client(name=demo_client_name, email=demo_email)
|
||||
session.add(demo_client)
|
||||
session.flush() # Get the client ID
|
||||
|
||||
@ -66,7 +69,7 @@ def create_demo_client_and_user():
|
||||
client_id=demo_client.id,
|
||||
is_admin=False,
|
||||
is_active=True,
|
||||
email_verified=True
|
||||
email_verified=True,
|
||||
)
|
||||
|
||||
# Add and commit
|
||||
@ -88,6 +91,7 @@ def create_demo_client_and_user():
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = create_demo_client_and_user()
|
||||
sys.exit(0 if success else 1)
|
@ -1,184 +0,0 @@
|
||||
"""
|
||||
Script to create example contacts for the demo client:
|
||||
- Contacts with conversation history
|
||||
- Different client profiles
|
||||
- Fake data for demonstration
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import json
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from dotenv import load_dotenv
|
||||
from src.models.models import Contact, User, Client
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_demo_contacts():
|
||||
"""Create example contacts for the demo client"""
|
||||
try:
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Get database settings
|
||||
db_url = os.getenv("POSTGRES_CONNECTION_STRING")
|
||||
if not db_url:
|
||||
logger.error("Environment variable POSTGRES_CONNECTION_STRING not defined")
|
||||
return False
|
||||
|
||||
# Connect to the database
|
||||
engine = create_engine(db_url)
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
try:
|
||||
# Get demo client by user email
|
||||
demo_email = os.getenv("DEMO_EMAIL", "demo@example.com")
|
||||
demo_user = session.query(User).filter(User.email == demo_email).first()
|
||||
|
||||
if not demo_user or not demo_user.client_id:
|
||||
logger.error(
|
||||
f"Demo user not found or not associated with a client: {demo_email}"
|
||||
)
|
||||
return False
|
||||
|
||||
client_id = demo_user.client_id
|
||||
|
||||
# Check if there are already contacts for this client
|
||||
existing_contacts = (
|
||||
session.query(Contact).filter(Contact.client_id == client_id).all()
|
||||
)
|
||||
if existing_contacts:
|
||||
logger.info(
|
||||
f"There are already {len(existing_contacts)} contacts for the client {client_id}"
|
||||
)
|
||||
return True
|
||||
|
||||
# Example contact definitions
|
||||
contacts = [
|
||||
{
|
||||
"name": "Maria Silva",
|
||||
"ext_id": "5511999998888",
|
||||
"meta": {
|
||||
"source": "whatsapp",
|
||||
"tags": ["cliente_vip", "suporte_premium"],
|
||||
"location": "São Paulo, SP",
|
||||
"last_contact": "2023-08-15T14:30:00Z",
|
||||
"account_details": {
|
||||
"customer_since": "2020-03-10",
|
||||
"plan": "Enterprise",
|
||||
"payment_status": "active",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "João Santos",
|
||||
"ext_id": "5511988887777",
|
||||
"meta": {
|
||||
"source": "whatsapp",
|
||||
"tags": ["prospecção", "demo_solicitada"],
|
||||
"location": "Rio de Janeiro, RJ",
|
||||
"last_contact": "2023-09-20T10:15:00Z",
|
||||
"interests": ["automação", "marketing", "chatbots"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Ana Oliveira",
|
||||
"ext_id": "5511977776666",
|
||||
"meta": {
|
||||
"source": "telegram",
|
||||
"tags": ["suporte_técnico", "problema_resolvido"],
|
||||
"location": "Belo Horizonte, MG",
|
||||
"last_contact": "2023-09-25T16:45:00Z",
|
||||
"support_cases": [
|
||||
{
|
||||
"id": "SUP-2023-1234",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
},
|
||||
{
|
||||
"id": "SUP-2023-1567",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Carlos Pereira",
|
||||
"ext_id": "5511966665555",
|
||||
"meta": {
|
||||
"source": "whatsapp",
|
||||
"tags": ["cancelamento", "retenção"],
|
||||
"location": "Porto Alegre, RS",
|
||||
"last_contact": "2023-09-10T09:30:00Z",
|
||||
"account_details": {
|
||||
"customer_since": "2019-05-22",
|
||||
"plan": "Professional",
|
||||
"payment_status": "overdue",
|
||||
"invoice_pending": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Fernanda Lima",
|
||||
"ext_id": "5511955554444",
|
||||
"meta": {
|
||||
"source": "telegram",
|
||||
"tags": ["parceiro", "integrador"],
|
||||
"location": "Curitiba, PR",
|
||||
"last_contact": "2023-09-18T14:00:00Z",
|
||||
"partner_details": {
|
||||
"company": "TechSolutions Ltda",
|
||||
"partner_level": "Gold",
|
||||
"certified": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# Create the contacts
|
||||
for contact_data in contacts:
|
||||
contact = Contact(
|
||||
client_id=client_id,
|
||||
name=contact_data["name"],
|
||||
ext_id=contact_data["ext_id"],
|
||||
meta=contact_data["meta"],
|
||||
)
|
||||
|
||||
session.add(contact)
|
||||
logger.info(
|
||||
f"Contact '{contact_data['name']}' created for the client {client_id}"
|
||||
)
|
||||
|
||||
session.commit()
|
||||
logger.info(
|
||||
f"All example contacts were created successfully for the client {client_id}"
|
||||
)
|
||||
return True
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
session.rollback()
|
||||
logger.error(
|
||||
f"Database error when creating example contacts: {str(e)}"
|
||||
)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error when creating example contacts: {str(e)}")
|
||||
return False
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = create_demo_contacts()
|
||||
sys.exit(0 if success else 1)
|
@ -38,11 +38,11 @@ router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
@router.websocket("/ws/{agent_id}/{contact_id}")
|
||||
@router.websocket("/ws/{agent_id}/{external_id}")
|
||||
async def websocket_chat(
|
||||
websocket: WebSocket,
|
||||
agent_id: str,
|
||||
contact_id: str,
|
||||
external_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
@ -81,7 +81,7 @@ async def websocket_chat(
|
||||
await verify_user_client(payload, db, agent.client_id)
|
||||
|
||||
logger.info(
|
||||
f"WebSocket connection established for agent {agent_id} and contact {contact_id}"
|
||||
f"WebSocket connection established for agent {agent_id} and external_id {external_id}"
|
||||
)
|
||||
|
||||
while True:
|
||||
@ -95,7 +95,7 @@ async def websocket_chat(
|
||||
|
||||
async for chunk in run_agent_stream(
|
||||
agent_id=agent_id,
|
||||
contact_id=contact_id,
|
||||
external_id=external_id,
|
||||
message=message,
|
||||
session_service=session_service,
|
||||
artifacts_service=artifacts_service,
|
||||
@ -162,7 +162,7 @@ async def chat(
|
||||
try:
|
||||
final_response_text = await run_agent(
|
||||
request.agent_id,
|
||||
request.contact_id,
|
||||
request.external_id,
|
||||
request.message,
|
||||
session_service,
|
||||
artifacts_service,
|
||||
|
@ -1,122 +0,0 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from src.config.database import get_db
|
||||
from typing import List
|
||||
import uuid
|
||||
from src.core.jwt_middleware import (
|
||||
get_jwt_token,
|
||||
verify_user_client,
|
||||
)
|
||||
from src.schemas.schemas import (
|
||||
Contact,
|
||||
ContactCreate,
|
||||
)
|
||||
from src.services import (
|
||||
contact_service,
|
||||
)
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/contacts",
|
||||
tags=["contacts"],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/", response_model=Contact, status_code=status.HTTP_201_CREATED)
|
||||
async def create_contact(
|
||||
contact: ContactCreate,
|
||||
db: Session = Depends(get_db),
|
||||
payload: dict = Depends(get_jwt_token),
|
||||
):
|
||||
# Verify if the user has access to the contact's client
|
||||
await verify_user_client(payload, db, contact.client_id)
|
||||
|
||||
return contact_service.create_contact(db, contact)
|
||||
|
||||
|
||||
@router.get("/{client_id}", response_model=List[Contact])
|
||||
async def read_contacts(
|
||||
client_id: uuid.UUID,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
payload: dict = Depends(get_jwt_token),
|
||||
):
|
||||
# Verify if the user has access to this client's data
|
||||
await verify_user_client(payload, db, client_id)
|
||||
|
||||
return contact_service.get_contacts_by_client(db, client_id, skip, limit)
|
||||
|
||||
|
||||
@router.get("/{contact_id}", response_model=Contact)
|
||||
async def read_contact(
|
||||
contact_id: uuid.UUID,
|
||||
db: Session = Depends(get_db),
|
||||
payload: dict = Depends(get_jwt_token),
|
||||
):
|
||||
db_contact = contact_service.get_contact(db, contact_id)
|
||||
if db_contact is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
|
||||
)
|
||||
|
||||
# Verify if the user has access to the contact's client
|
||||
await verify_user_client(payload, db, db_contact.client_id)
|
||||
|
||||
return db_contact
|
||||
|
||||
|
||||
@router.put("/{contact_id}", response_model=Contact)
|
||||
async def update_contact(
|
||||
contact_id: uuid.UUID,
|
||||
contact: ContactCreate,
|
||||
db: Session = Depends(get_db),
|
||||
payload: dict = Depends(get_jwt_token),
|
||||
):
|
||||
# Get the current contact
|
||||
db_current_contact = contact_service.get_contact(db, contact_id)
|
||||
if db_current_contact is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
|
||||
)
|
||||
|
||||
# Verify if the user has access to the contact's client
|
||||
await verify_user_client(payload, db, db_current_contact.client_id)
|
||||
|
||||
# Verify if the user is trying to change the client
|
||||
if contact.client_id != db_current_contact.client_id:
|
||||
# Verify if the user has access to the new client as well
|
||||
await verify_user_client(payload, db, contact.client_id)
|
||||
|
||||
db_contact = contact_service.update_contact(db, contact_id, contact)
|
||||
if db_contact is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
|
||||
)
|
||||
return db_contact
|
||||
|
||||
|
||||
@router.delete("/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_contact(
|
||||
contact_id: uuid.UUID,
|
||||
db: Session = Depends(get_db),
|
||||
payload: dict = Depends(get_jwt_token),
|
||||
):
|
||||
# Get the contact
|
||||
db_contact = contact_service.get_contact(db, contact_id)
|
||||
if db_contact is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
|
||||
)
|
||||
|
||||
# Verify if the user has access to the contact's client
|
||||
await verify_user_client(payload, db, db_contact.client_id)
|
||||
|
||||
if not contact_service.delete_contact(db, contact_id):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
|
||||
)
|
@ -34,21 +34,6 @@ def get_redis_config():
|
||||
}
|
||||
|
||||
|
||||
def get_a2a_config():
|
||||
"""
|
||||
Get A2A-specific cache TTL values from environment variables.
|
||||
|
||||
Returns:
|
||||
dict: A2A TTL configuration parameters
|
||||
"""
|
||||
return {
|
||||
"task_ttl": int(os.getenv("A2A_TASK_TTL", 3600)),
|
||||
"history_ttl": int(os.getenv("A2A_HISTORY_TTL", 86400)),
|
||||
"push_notification_ttl": int(os.getenv("A2A_PUSH_NOTIFICATION_TTL", 3600)),
|
||||
"sse_client_ttl": int(os.getenv("A2A_SSE_CLIENT_TTL", 300)),
|
||||
}
|
||||
|
||||
|
||||
def create_redis_pool(config=None):
|
||||
"""
|
||||
Create and return a Redis connection pool.
|
||||
|
@ -47,7 +47,7 @@ class Settings(BaseSettings):
|
||||
# JWT settings
|
||||
JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", secrets.token_urlsafe(32))
|
||||
JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256")
|
||||
JWT_EXPIRATION_TIME: int = int(os.getenv("JWT_EXPIRATION_TIME", 30))
|
||||
JWT_EXPIRATION_TIME: int = int(os.getenv("JWT_EXPIRATION_TIME", 3600))
|
||||
|
||||
# SendGrid settings
|
||||
SENDGRID_API_KEY: str = os.getenv("SENDGRID_API_KEY", "")
|
||||
|
@ -18,7 +18,6 @@ import src.api.admin_routes
|
||||
import src.api.chat_routes
|
||||
import src.api.session_routes
|
||||
import src.api.agent_routes
|
||||
import src.api.contact_routes
|
||||
import src.api.mcp_server_routes
|
||||
import src.api.tool_routes
|
||||
import src.api.client_routes
|
||||
@ -70,7 +69,6 @@ admin_router = src.api.admin_routes.router
|
||||
chat_router = src.api.chat_routes.router
|
||||
session_router = src.api.session_routes.router
|
||||
agent_router = src.api.agent_routes.router
|
||||
contact_router = src.api.contact_routes.router
|
||||
mcp_server_router = src.api.mcp_server_routes.router
|
||||
tool_router = src.api.tool_routes.router
|
||||
client_router = src.api.client_routes.router
|
||||
@ -85,7 +83,6 @@ app.include_router(client_router, prefix=API_PREFIX)
|
||||
app.include_router(chat_router, prefix=API_PREFIX)
|
||||
app.include_router(session_router, prefix=API_PREFIX)
|
||||
app.include_router(agent_router, prefix=API_PREFIX)
|
||||
app.include_router(contact_router, prefix=API_PREFIX)
|
||||
app.include_router(a2a_router, prefix=API_PREFIX)
|
||||
|
||||
|
||||
|
@ -51,18 +51,6 @@ class User(Base):
|
||||
)
|
||||
|
||||
|
||||
class Contact(Base):
|
||||
__tablename__ = "contacts"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
client_id = Column(UUID(as_uuid=True), ForeignKey("clients.id", ondelete="CASCADE"))
|
||||
ext_id = Column(String)
|
||||
name = Column(String)
|
||||
meta = Column(JSON, default={})
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
|
||||
class Agent(Base):
|
||||
__tablename__ = "agents"
|
||||
|
||||
|
@ -8,8 +8,8 @@ class ChatRequest(BaseModel):
|
||||
agent_id: str = Field(
|
||||
..., description="ID of the agent that will process the message"
|
||||
)
|
||||
contact_id: str = Field(
|
||||
..., description="ID of the contact that will process the message"
|
||||
external_id: str = Field(
|
||||
..., description="ID of the external_id that will process the message"
|
||||
)
|
||||
message: str = Field(..., description="User message")
|
||||
|
||||
|
@ -33,24 +33,6 @@ class Client(ClientBase):
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ContactBase(BaseModel):
|
||||
ext_id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
meta: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class ContactCreate(ContactBase):
|
||||
client_id: UUID
|
||||
|
||||
|
||||
class Contact(ContactBase):
|
||||
id: UUID
|
||||
client_id: UUID
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class AgentBase(BaseModel):
|
||||
name: Optional[str] = Field(
|
||||
None, description="Agent name (no spaces or special characters)"
|
||||
|
@ -315,13 +315,13 @@ class A2ATaskManager:
|
||||
)
|
||||
|
||||
# Collect the chunks of the agent's response
|
||||
contact_id = task_params.sessionId
|
||||
external_id = task_params.sessionId
|
||||
full_response = ""
|
||||
|
||||
# We use the same streaming function used in the WebSocket
|
||||
async for chunk in run_agent_stream(
|
||||
agent_id=str(agent.id),
|
||||
contact_id=contact_id,
|
||||
external_id=external_id,
|
||||
message=query,
|
||||
session_service=session_service,
|
||||
artifacts_service=artifacts_service,
|
||||
@ -438,13 +438,13 @@ class A2ATaskManager:
|
||||
async def _run_agent(self, agent: Agent, query: str, session_id: str) -> str:
|
||||
"""Executes the agent to process the user query."""
|
||||
try:
|
||||
# We use the session_id as contact_id to maintain the conversation continuity
|
||||
contact_id = session_id
|
||||
# We use the session_id as external_id to maintain the conversation continuity
|
||||
external_id = session_id
|
||||
|
||||
# We call the same function used in the chat API
|
||||
final_response = await run_agent(
|
||||
agent_id=str(agent.id),
|
||||
contact_id=contact_id,
|
||||
external_id=external_id,
|
||||
message=query,
|
||||
session_service=session_service,
|
||||
artifacts_service=artifacts_service,
|
||||
|
@ -16,7 +16,7 @@ logger = setup_logger(__name__)
|
||||
|
||||
async def run_agent(
|
||||
agent_id: str,
|
||||
contact_id: str,
|
||||
external_id: str,
|
||||
message: str,
|
||||
session_service: DatabaseSessionService,
|
||||
artifacts_service: InMemoryArtifactService,
|
||||
@ -27,7 +27,9 @@ async def run_agent(
|
||||
):
|
||||
exit_stack = None
|
||||
try:
|
||||
logger.info(f"Starting execution of agent {agent_id} for contact {contact_id}")
|
||||
logger.info(
|
||||
f"Starting execution of agent {agent_id} for external_id {external_id}"
|
||||
)
|
||||
logger.info(f"Received message: {message}")
|
||||
|
||||
get_root_agent = get_agent(db, agent_id)
|
||||
@ -50,22 +52,22 @@ async def run_agent(
|
||||
artifact_service=artifacts_service,
|
||||
memory_service=memory_service,
|
||||
)
|
||||
adk_session_id = contact_id + "_" + agent_id
|
||||
adk_session_id = external_id + "_" + agent_id
|
||||
if session_id is None:
|
||||
session_id = adk_session_id
|
||||
|
||||
logger.info(f"Searching session for contact {contact_id}")
|
||||
logger.info(f"Searching session for external_id {external_id}")
|
||||
session = session_service.get_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
if session is None:
|
||||
logger.info(f"Creating new session for contact {contact_id}")
|
||||
logger.info(f"Creating new session for external_id {external_id}")
|
||||
session = session_service.create_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
@ -80,7 +82,7 @@ async def run_agent(
|
||||
async def process_events():
|
||||
try:
|
||||
events_async = agent_runner.run_async(
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
new_message=content,
|
||||
)
|
||||
@ -139,7 +141,7 @@ async def run_agent(
|
||||
# Add the session to memory after completion
|
||||
completed_session = session_service.get_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
@ -180,7 +182,7 @@ async def run_agent(
|
||||
|
||||
async def run_agent_stream(
|
||||
agent_id: str,
|
||||
contact_id: str,
|
||||
external_id: str,
|
||||
message: str,
|
||||
session_service: DatabaseSessionService,
|
||||
artifacts_service: InMemoryArtifactService,
|
||||
@ -190,7 +192,7 @@ async def run_agent_stream(
|
||||
) -> AsyncGenerator[str, None]:
|
||||
try:
|
||||
logger.info(
|
||||
f"Starting streaming execution of agent {agent_id} for contact {contact_id}"
|
||||
f"Starting streaming execution of agent {agent_id} for external_id {external_id}"
|
||||
)
|
||||
logger.info(f"Received message: {message}")
|
||||
|
||||
@ -214,22 +216,22 @@ async def run_agent_stream(
|
||||
artifact_service=artifacts_service,
|
||||
memory_service=memory_service,
|
||||
)
|
||||
adk_session_id = contact_id + "_" + agent_id
|
||||
adk_session_id = external_id + "_" + agent_id
|
||||
if session_id is None:
|
||||
session_id = adk_session_id
|
||||
|
||||
logger.info(f"Searching session for contact {contact_id}")
|
||||
logger.info(f"Searching session for external_id {external_id}")
|
||||
session = session_service.get_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
if session is None:
|
||||
logger.info(f"Creating new session for contact {contact_id}")
|
||||
logger.info(f"Creating new session for external_id {external_id}")
|
||||
session = session_service.create_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
@ -238,7 +240,7 @@ async def run_agent_stream(
|
||||
|
||||
try:
|
||||
events_async = agent_runner.run_async(
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
new_message=content,
|
||||
)
|
||||
@ -252,7 +254,7 @@ async def run_agent_stream(
|
||||
|
||||
completed_session = session_service.get_session(
|
||||
app_name=agent_id,
|
||||
user_id=contact_id,
|
||||
user_id=external_id,
|
||||
session_id=adk_session_id,
|
||||
)
|
||||
|
||||
|
@ -1,109 +0,0 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from fastapi import HTTPException, status
|
||||
from src.models.models import Contact
|
||||
from src.schemas.schemas import ContactCreate
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_contact(db: Session, contact_id: uuid.UUID) -> Optional[Contact]:
|
||||
"""Search for a contact by ID"""
|
||||
try:
|
||||
contact = db.query(Contact).filter(Contact.id == contact_id).first()
|
||||
if not contact:
|
||||
logger.warning(f"Contact not found: {contact_id}")
|
||||
return None
|
||||
return contact
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error searching for contact {contact_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error searching for contact",
|
||||
)
|
||||
|
||||
|
||||
def get_contacts_by_client(
|
||||
db: Session, client_id: uuid.UUID, skip: int = 0, limit: int = 100
|
||||
) -> List[Contact]:
|
||||
"""Search for contacts of a client with pagination"""
|
||||
try:
|
||||
return (
|
||||
db.query(Contact)
|
||||
.filter(Contact.client_id == client_id)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error searching for contacts of client {client_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error searching for contacts",
|
||||
)
|
||||
|
||||
|
||||
def create_contact(db: Session, contact: ContactCreate) -> Contact:
|
||||
"""Create a new contact"""
|
||||
try:
|
||||
db_contact = Contact(**contact.model_dump())
|
||||
db.add(db_contact)
|
||||
db.commit()
|
||||
db.refresh(db_contact)
|
||||
logger.info(f"Contact created successfully: {db_contact.id}")
|
||||
return db_contact
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error creating contact: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error creating contact",
|
||||
)
|
||||
|
||||
|
||||
def update_contact(
|
||||
db: Session, contact_id: uuid.UUID, contact: ContactCreate
|
||||
) -> Optional[Contact]:
|
||||
"""Update an existing contact"""
|
||||
try:
|
||||
db_contact = get_contact(db, contact_id)
|
||||
if not db_contact:
|
||||
return None
|
||||
|
||||
for key, value in contact.model_dump().items():
|
||||
setattr(db_contact, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_contact)
|
||||
logger.info(f"Contact updated successfully: {contact_id}")
|
||||
return db_contact
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error updating contact {contact_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error updating contact",
|
||||
)
|
||||
|
||||
|
||||
def delete_contact(db: Session, contact_id: uuid.UUID) -> bool:
|
||||
"""Remove a contact"""
|
||||
try:
|
||||
db_contact = get_contact(db, contact_id)
|
||||
if not db_contact:
|
||||
return False
|
||||
|
||||
db.delete(db_contact)
|
||||
db.commit()
|
||||
logger.info(f"Contact removed successfully: {contact_id}")
|
||||
return True
|
||||
except SQLAlchemyError as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error removing contact {contact_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error removing contact",
|
||||
)
|
@ -197,10 +197,32 @@ class WorkflowAgent(BaseAgent):
|
||||
print(f"\n🔄 CONDITION: {label} (Cycle {cycle_count})")
|
||||
|
||||
content = state.get("content", [])
|
||||
print(f"Evaluating condition for content: '{content}'")
|
||||
conversation_history = state.get("conversation_history", [])
|
||||
|
||||
# Obter apenas o evento mais recente para avaliação
|
||||
latest_event = None
|
||||
if content and len(content) > 0:
|
||||
# Ignorar eventos gerados por nós de condição para avaliação
|
||||
for event in reversed(content):
|
||||
# Verificar se é um evento gerado pelo condition_node
|
||||
if (
|
||||
event.author != "agent"
|
||||
or not hasattr(event.content, "parts")
|
||||
or not event.content.parts
|
||||
):
|
||||
latest_event = event
|
||||
break
|
||||
if latest_event:
|
||||
print(
|
||||
f"Avaliando condição apenas para o evento mais recente: '{latest_event}'"
|
||||
)
|
||||
|
||||
# Usar apenas o evento mais recente para avaliação de condição
|
||||
evaluation_state = state.copy()
|
||||
if latest_event:
|
||||
evaluation_state["content"] = [latest_event]
|
||||
|
||||
session_id = state.get("session_id", "")
|
||||
conversation_history = state.get("conversation_history", [])
|
||||
|
||||
# Check all conditions
|
||||
conditions_met = []
|
||||
@ -213,9 +235,9 @@ class WorkflowAgent(BaseAgent):
|
||||
expected_value = condition_data.get("value")
|
||||
|
||||
print(
|
||||
f" Checking if {field} {operator} '{expected_value}' (current value: '{state.get(field, '')}')"
|
||||
f" Checking if {field} {operator} '{expected_value}' (current value: '{evaluation_state.get(field, '')}')"
|
||||
)
|
||||
if self._evaluate_condition(condition, state):
|
||||
if self._evaluate_condition(condition, evaluation_state):
|
||||
conditions_met.append(condition_id)
|
||||
condition_details.append(
|
||||
f"{field} {operator} '{expected_value}' ✅"
|
||||
@ -474,14 +496,35 @@ class WorkflowAgent(BaseAgent):
|
||||
# If it's a condition node, evaluate the conditions
|
||||
if node_id in condition_nodes:
|
||||
conditions = condition_nodes[node_id]
|
||||
any_condition_met = False
|
||||
|
||||
for condition in conditions:
|
||||
condition_id = condition.get("id")
|
||||
|
||||
# Get latest event for evaluation, ignoring condition node informational events
|
||||
content = state.get("content", [])
|
||||
latest_event = None
|
||||
for event in reversed(content):
|
||||
# Skip events generated by condition nodes
|
||||
if (
|
||||
event.author != "agent"
|
||||
or not hasattr(event.content, "parts")
|
||||
or not event.content.parts
|
||||
):
|
||||
latest_event = event
|
||||
break
|
||||
|
||||
evaluation_state = state.copy()
|
||||
if latest_event:
|
||||
evaluation_state["content"] = [latest_event]
|
||||
|
||||
# Check if the condition is met
|
||||
is_condition_met = self._evaluate_condition(condition, state)
|
||||
is_condition_met = self._evaluate_condition(
|
||||
condition, evaluation_state
|
||||
)
|
||||
|
||||
if is_condition_met:
|
||||
any_condition_met = True
|
||||
print(
|
||||
f"Condition {condition_id} met. Moving to the next node."
|
||||
)
|
||||
@ -498,12 +541,20 @@ class WorkflowAgent(BaseAgent):
|
||||
)
|
||||
|
||||
# If no condition is met, use the bottom-handle if available
|
||||
if node_id in edges_map and "bottom-handle" in edges_map[node_id]:
|
||||
print("No condition met. Using default path (bottom-handle).")
|
||||
return edges_map[node_id]["bottom-handle"]
|
||||
else:
|
||||
print("No condition met and no default path. Closing the flow.")
|
||||
return END
|
||||
if not any_condition_met:
|
||||
if (
|
||||
node_id in edges_map
|
||||
and "bottom-handle" in edges_map[node_id]
|
||||
):
|
||||
print(
|
||||
"No condition met. Using default path (bottom-handle)."
|
||||
)
|
||||
return edges_map[node_id]["bottom-handle"]
|
||||
else:
|
||||
print(
|
||||
"No condition met and no default path. Closing the flow."
|
||||
)
|
||||
return END
|
||||
|
||||
# For regular nodes, simply follow the first available connection
|
||||
if node_id in edges_map:
|
||||
|
Loading…
Reference in New Issue
Block a user