Skip to content

Commit 7e3afd4

Browse files
feat(pedidos): fazer pedidos pelo chatbot
1 parent 1246395 commit 7e3afd4

1 file changed

Lines changed: 166 additions & 32 deletions

File tree

api/chatbot/agents/order_taking_agent.py

Lines changed: 166 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from copy import deepcopy
77
from typing import List
88

9-
from api_types import MessageDict, OrderTakingAgentResponse
9+
from api_types import MessageDict, OrderTakingAgentResponse, OrderItem
1010
from dotenv import load_dotenv
1111
from openai import OpenAI
1212

@@ -31,11 +31,123 @@ def __init__(self):
3131
self.client = OpenAI(
3232
api_key=os.getenv("CHATBOT_API_KEY"),
3333
base_url=os.getenv("CHATBOT_URL")
34-
)
35-
# Modelo de LLM
36-
self.model_name: str = os.getenv("MODEL_NAME", "") # Agente de recomendação
34+
) # Modelo de LLM
35+
self.model_name: str = os.getenv(
36+
"MODEL_NAME", "") # Agente de recomendação
3737
# TODO: Implementar agente de recomendação
3838

39+
def finalizar_pedido(self, pedido: List[OrderItem], token_cliente: str = "token_teste_chatbot", api_url: str = "http://localhost:8000") -> dict:
40+
'''
41+
Finaliza o pedido enviando para a API.
42+
43+
:param pedido: Lista de itens do pedido
44+
:param token_cliente: Token do cliente (para teste)
45+
:param api_url: URL base da API
46+
:return: Dicionário com resultado da operação
47+
'''
48+
try:
49+
# Converter formato do pedido para o esperado pela API
50+
itens_api = []
51+
for item in pedido:
52+
# Buscar ID do item no cardápio
53+
id_item = self.buscar_id_item(item.get('item', ''))
54+
if id_item:
55+
itens_api.append({
56+
"id_item": id_item,
57+
"quantidade": item.get('quantidade', 1),
58+
"preco": item.get('preco', 0.0),
59+
"observacoes": item.get('observacoes', '')
60+
})
61+
62+
if not itens_api:
63+
return {
64+
"sucesso": False,
65+
"erro": "Nenhum item válido encontrado no pedido"
66+
}
67+
68+
# Dados para a API
69+
payload = {
70+
"body": json.dumps({
71+
"token": token_cliente,
72+
"itens": itens_api
73+
})
74+
}
75+
76+
# Headers
77+
headers = {
78+
"Content-Type": "application/json"
79+
}
80+
81+
# Fazer requisição para a API
82+
response = requests.post(
83+
f"{api_url}/pedido",
84+
json=payload,
85+
headers=headers,
86+
timeout=10
87+
)
88+
89+
if response.status_code == 200:
90+
resultado = response.json()
91+
return {
92+
"sucesso": True,
93+
"pedido_id": resultado.get("pedido", {}).get("id_pedido", ""),
94+
"codigo_retirada": resultado.get("pedido", {}).get("codigo_retirada", ""),
95+
"mensagem": "Pedido finalizado com sucesso!"
96+
}
97+
else:
98+
return {
99+
"sucesso": False,
100+
"erro": f"Erro na API: {response.status_code} - {response.text}"
101+
}
102+
103+
except Exception as e:
104+
return {
105+
"sucesso": False,
106+
"erro": f"Erro ao finalizar pedido: {str(e)}"
107+
}
108+
109+
def buscar_id_item(self, nome_item: str) -> str:
110+
'''
111+
Busca o ID de um item pelo nome no cardápio.
112+
113+
:param nome_item: Nome do item para buscar
114+
:return: ID do item ou string vazia se não encontrado
115+
'''
116+
try:
117+
# Obter cardápio atualizado
118+
id_restaurante = "44c57a5e-ced2-4938-ba1d-108a60a60ea1"
119+
120+
payload = {
121+
"body": json.dumps({
122+
"id_restaurante": id_restaurante
123+
})
124+
}
125+
126+
headers = {
127+
"Content-Type": "application/json"
128+
}
129+
130+
response = requests.post(
131+
"http://localhost:8000/cozinha/pratos",
132+
json=payload,
133+
headers=headers,
134+
timeout=10
135+
)
136+
137+
if response.status_code == 200:
138+
data = response.json()
139+
pratos = data.get("pratos", [])
140+
141+
# Buscar item pelo nome (case insensitive)
142+
for prato in pratos:
143+
if prato.get('nome', '').lower() == nome_item.lower():
144+
return prato.get('id_item', '')
145+
146+
return ""
147+
148+
except Exception:
149+
return ""
150+
39151
# Obter resposta
40152
def get_response(self, messages: List[MessageDict]) -> MessageDict:
41153
'''
@@ -46,7 +158,8 @@ def get_response(self, messages: List[MessageDict]) -> MessageDict:
46158
messages = deepcopy(messages) # Evitar efeitos colaterais
47159

48160
# Menu do restaurante - Obter via API
49-
id_restaurante = "44c57a5e-ced2-4938-ba1d-108a60a60ea1" # ID padrão da Cafeteria Nova Geração
161+
# ID padrão da Cafeteria Nova Geração
162+
id_restaurante = "44c57a5e-ced2-4938-ba1d-108a60a60ea1"
50163
cardapio = self.obter_cardapio_restaurante(id_restaurante)
51164

52165
# System prompt
@@ -199,9 +312,8 @@ def postprocess(self, output: str, recomendacao_solicitada_antes: bool = False)
199312
required_keys = ['pedido', 'resposta', 'etapa']
200313
for key in required_keys:
201314
if key not in output_dict:
315+
# Processamento do pedido
202316
return self.criar_output_padrao("Desculpe, não consegui processar seu pedido. Por favor, tente novamente.")
203-
204-
# Processamento do pedido
205317
pedido = output_dict.get('pedido', [])
206318
if isinstance(pedido, str): # Verificar se é uma string
207319
try:
@@ -214,6 +326,27 @@ def postprocess(self, output: str, recomendacao_solicitada_antes: bool = False)
214326
# Obter resposta do agente
215327
response = output_dict.get('resposta', '').strip()
216328

329+
# Verificar se está na etapa final (6) e há pedidos para finalizar
330+
etapa = int(output_dict.get('etapa', 1))
331+
pedido = output_dict.get('pedido', [])
332+
333+
# Se está na etapa 6 e há itens no pedido, finalizar pedido
334+
if etapa >= 6 and pedido:
335+
resultado_pedido = self.finalizar_pedido(pedido)
336+
337+
if resultado_pedido.get('sucesso'):
338+
codigo_retirada = resultado_pedido.get(
339+
'codigo_retirada', '')
340+
response += "\n\n✅ **Pedido finalizado com sucesso!**"
341+
if codigo_retirada:
342+
response += f"\n🎫 **Código de retirada: {codigo_retirada}**"
343+
response += "\n📋 Seu pedido foi enviado para a cozinha e estará pronto em breve."
344+
response += "\n🕒 Você receberá uma notificação quando estiver pronto para retirada."
345+
else:
346+
erro = resultado_pedido.get('erro', 'Erro desconhecido')
347+
response += f"\n\n❌ **Erro ao finalizar pedido:** {erro}"
348+
response += "\n💡 Você pode tentar novamente ou entrar em contato com o restaurante."
349+
217350
except json.JSONDecodeError:
218351
return self.criar_output_padrao(
219352
"Desculpe, ocorreu um erro ao processar seu pedido. Por favor, tente novamente."
@@ -233,7 +366,7 @@ def postprocess(self, output: str, recomendacao_solicitada_antes: bool = False)
233366
def obter_cardapio_restaurante(self, id_restaurante: str, api_url: str = "http://localhost:8000") -> str:
234367
"""
235368
Obtém o cardápio do restaurante via API e formata como string.
236-
369+
237370
:param id_restaurante: ID do restaurante
238371
:param api_url: URL base da API (padrão: localhost:8000)
239372
:return: String formatada com o cardápio
@@ -245,50 +378,50 @@ def obter_cardapio_restaurante(self, id_restaurante: str, api_url: str = "http:/
245378
"id_restaurante": id_restaurante
246379
})
247380
}
248-
381+
249382
# Headers para a requisição
250383
headers = {
251384
"Content-Type": "application/json"
252385
}
253-
386+
254387
# Fazer requisição POST para o endpoint
255388
response = requests.post(
256389
f"{api_url}/cozinha/pratos",
257390
json=payload,
258391
headers=headers,
259392
timeout=10
260393
)
261-
394+
262395
# Verificar se a requisição foi bem-sucedida
263396
response.raise_for_status()
264-
397+
265398
# Extrair dados da resposta
266399
data = response.json()
267-
400+
268401
if "pratos" not in data:
269402
return "❌ Erro: Não foi possível obter o cardápio do restaurante."
270-
403+
271404
pratos = data["pratos"]
272-
405+
273406
if not pratos:
274407
return "ℹ️ Este restaurante não possui produtos cadastrados no momento."
275-
408+
276409
# Formatar cardápio
277410
cardapio_formatado = "📋 **CARDÁPIO DO RESTAURANTE**\n\n"
278-
411+
279412
# Agrupar por categoria
280413
categorias = {}
281414
for prato in pratos:
282415
categoria = prato.get('categoria', 'Sem categoria')
283416
if categoria not in categorias:
284417
categorias[categoria] = []
285418
categorias[categoria].append(prato)
286-
419+
287420
# Formatar cada categoria
288421
for categoria, itens in categorias.items():
289422
cardapio_formatado += f"🍽️ **{categoria.upper()}**\n"
290423
cardapio_formatado += "-" * 50 + "\n"
291-
424+
292425
for item in itens:
293426
nome = item.get('nome', 'Nome não disponível')
294427
id_item = item.get('id_item', 'ID não disponível')
@@ -297,45 +430,46 @@ def obter_cardapio_restaurante(self, id_restaurante: str, api_url: str = "http:/
297430
descricao = item.get('descricao', '')
298431
estoque = item.get('estoque', 0)
299432
avaliacao = item.get('avaliacao', None)
300-
433+
301434
# Linha principal do produto
302435
linha_produto = f"{nome} | ID: {id_item} | Preço: R$ {preco}"
303-
436+
304437
# Adicionar preço especial se existir
305438
if preco_especial and preco_especial != preco:
306439
linha_produto += f" | Preço especial (para alunos, professores e membros do Colégio Poliedro): R$ {preco_especial}"
307-
440+
308441
cardapio_formatado += linha_produto + "\n"
309-
442+
310443
# Adicionar descrição se existir
311444
if descricao:
312445
cardapio_formatado += f" 📝 {descricao}\n"
313-
446+
314447
# Adicionar informações extras
315448
info_extra = []
316449
if estoque is not None:
317450
if estoque > 0:
318-
info_extra.append(f"✅ Em estoque ({estoque} unidades)")
451+
info_extra.append(
452+
f"✅ Em estoque ({estoque} unidades)")
319453
else:
320454
info_extra.append("❌ Sem estoque")
321-
455+
322456
if avaliacao is not None:
323457
info_extra.append(f"⭐ Avaliação: {avaliacao:.1f}/5")
324-
458+
325459
if info_extra:
326460
cardapio_formatado += f" {' | '.join(info_extra)}\n"
327-
461+
328462
cardapio_formatado += "\n"
329-
463+
330464
cardapio_formatado += "\n"
331-
465+
332466
# Adicionar informações finais
333467
total_produtos = len(pratos)
334468
cardapio_formatado += f"📊 **Total de produtos disponíveis: {total_produtos}**\n"
335469
cardapio_formatado += "💡 **Dica:** Mencione o nome ou ID do produto para fazer seu pedido!\n"
336-
470+
337471
return cardapio_formatado.strip()
338-
472+
339473
except requests.exceptions.RequestException as e:
340474
return f"❌ Erro de conexão com a API: {str(e)}"
341475
except json.JSONDecodeError as e:

0 commit comments

Comments
 (0)