You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
2.8 KiB
Python
88 lines
2.8 KiB
Python
from typing import Dict, Any, List
|
|
import vertexai
|
|
from vertexai.generative_models import GenerativeModel, Tool, FunctionDeclaration
|
|
from app.core.settings import settings
|
|
from app.models.tool_model import ToolDefinition
|
|
|
|
|
|
class LLMService:
|
|
|
|
def __init__(self):
|
|
vertexai.init(
|
|
project=settings.google_project_id,
|
|
location=settings.google_location
|
|
)
|
|
|
|
self.model = GenerativeModel("gemini-1.5-flash")
|
|
|
|
def build_vertex_tools(self, tools: List[ToolDefinition]): # Converte as Tools internas (ToolDefinition) para o formato que o Vertex AI entende.
|
|
|
|
vertex_tools = []
|
|
|
|
# Para cada Tool registrada no sistema (depende da proposta do cliente) criamos uma Tool do Vertex AI
|
|
for tool in tools:
|
|
vertex_tools.append(
|
|
Tool(
|
|
function_declarations=[
|
|
FunctionDeclaration(
|
|
name=tool.name,
|
|
description=tool.description,
|
|
parameters=tool.parameters
|
|
)
|
|
]
|
|
)
|
|
)
|
|
|
|
return vertex_tools
|
|
|
|
"""
|
|
Fluxo principal de geração de resposta.
|
|
|
|
Parâmetros:
|
|
- message: mensagem do usuário
|
|
- tools: lista de ferramentas disponíveis
|
|
- history: histórico da conversa (memória)
|
|
"""
|
|
async def generate_response(
|
|
self,
|
|
message: str,
|
|
tools: List[ToolDefinition],
|
|
history: List[Dict[str, Any]] = None
|
|
) -> Dict[str, Any]:
|
|
|
|
vertex_tools = self.build_vertex_tools(tools) # Convertendo tools para formato do Vertex
|
|
|
|
# Inicia uma sessão de chat com:
|
|
# - histórico (se existir)
|
|
# - ferramentas disponíveis
|
|
chat = self.model.start_chat(
|
|
history=history or []
|
|
)
|
|
|
|
response = chat.send_message(
|
|
message,
|
|
tools=vertex_tools
|
|
)
|
|
|
|
# Pegamos a primeira resposta candidata do modelo (a com maior coerência com o assunto)
|
|
# Estrutura interna:
|
|
# response.candidates -> lista de possíveis respostas
|
|
# content.parts -> partes da resposta
|
|
part = response.candidates[0].content.parts[0]
|
|
|
|
# Verificação se o modelo decidiu chamar alguma função, se decidiu, retornará o nome da função que ele quer executar e o argumento que ele extraiu da mensagem do usuário.
|
|
if part.function_call:
|
|
return {
|
|
"response": None,
|
|
"tool_call": {
|
|
"name": part.function_call.name,
|
|
"arguments": dict(part.function_call.args)
|
|
}
|
|
}
|
|
|
|
# Caso não ocorra a chamada de uma função, significa que o modelo respondeu diretamente em texto
|
|
return {
|
|
"response": response.text,
|
|
"tool_call": None
|
|
}
|