66from copy import deepcopy
77from typing import List
88
9- from api_types import MessageDict , OrderTakingAgentResponse
9+ from api_types import MessageDict , OrderTakingAgentResponse , OrderItem
1010from dotenv import load_dotenv
1111from 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