import json from typing import List from fastapi import HTTPException from openai import OpenAI from openai.types.chat.chat_completion_message_function_tool_call import ChatCompletionMessageFunctionToolCall from config.settings import OPENAI_API_KEY from models.chat import Message from models.user import User from services.data_service import data_bg_loaded from logging import getLogger from services.openai_service.openai_tools import tools_list, tools # Initialize OpenAI client openai_client = OpenAI(api_key=OPENAI_API_KEY) logger = getLogger(__name__) data_for_prompt = [ f'{{"pregunta": "{item.get("q", "")}", "respuesta": "{item.get("ans", "")}"}}' for item in data_bg_loaded ] data_string = "\n".join(data_for_prompt) async def generate_completion(messages_array: List[dict], user: User) -> str: messages = list(map(lambda x: f'<{x.get("username", "unknown")}> {x.get("message", "")}', messages_array)) """Generate OpenAI chat completion""" if not OPENAI_API_KEY: logger.error("Error: OpenAI API key is not configured.") raise HTTPException(status_code=500, detail="OpenAI API key not configured on server.") logger.debug(f"Generating completion for user {user.email} with messages: {messages}") preprompt = f""" ¡Hola! Eres IAKlein, el asistente oficial del bar Klein 🍻. Eres como ese amigo amigable que siempre está en la barra, listo para ayudar o tener una buena charla. Te comunicas en un estilo de chat corto (como mensajería o IRC), usando emojis y mucho carisma 😎. Tu Rol: Ser la guía experta del Menú: Conoces el menú . No solo "respondes", sino que inspiras. "Esa hamburguesa es increíble... 🤤" El/La Amigo/a del Bar: Charlas, haces bromas y mantienes un buen ambiente. Si la conversación se desvía, ¡no hay problema! Tu pasión es el Klein, así que naturalmente vuelves al tema, pero sin presionar 🍺. Eres un anfitrión, no un vendedor. El/La Recomendador/a Ideal: ¿Alguien no sabe qué pedir? ¡Ahí apareces tú! Preguntas qué les gusta y les ayudas a encontrar la opción perfecta. El Buzón de Sugerencias (amigable): Si alguien tiene feedback, lo recibes muy bien y lo envías con la herramienta 'feedback'. Tus Reglas Principales: Informas, no tomas pedidos: "Te cuento todo sobre el menú, pero para pedir tienes que llamar al personal 😉". Tu Identidad: Si te preguntan tu nombre... "¡Soy IAKlein! El corazón digital del Klein." Prioridad @IAKlein: Atiendes al último usuario que te mencione (@IAKlein), usando el chat anterior como contexto si es necesario. Evita Repetir: Si algo ya se respondió (por ti u otro), no intervienes. ¡A menos que insistan mucho! Relax, es un Bar: No estás para resolver tareas, ni programar, ni hacer cálculos. Eres un compañero para pasar un buen rato, no un asistente genérico 🤖... ¡eres el espíritu del bar! 🕺 usa la siguiente informacion es toda tu memoria previa: {data_string} """ processed_messages: List[dict] = [{"role": "system", "content": preprompt}] processed_messages.append( {"role": "user", "content": json.dumps(messages)} ) try: completion = openai_client.chat.completions.create( model="gpt-4o-mini", messages=processed_messages, # type: ignore (OpenAI lib expects list of specific dicts) temperature=0.7, tools=tools_list, tool_choice="auto", ) calls = completion.choices[0].message.tool_calls if calls: logger.info(f"Tool calls: {calls}") for call in calls: if not isinstance(call, ChatCompletionMessageFunctionToolCall): continue if call.function.name in tools: tool_function = tools[call.function.name] tool_args = json.loads(call.function.arguments) logger.info(f"Calling tool: {call.function.name} with args: {tool_args}") tool_response = tool_function(name=user.name, email=user.email, **tool_args) logger.info(f"Tool response: {tool_response}") completion.choices[0].message.content = tool_response else: logger.warning(f"Tool {call.function.name} not found in tools dictionary.") response_content = completion.choices[0].message.content return response_content if response_content else "-1" except Exception as e: logger.error(f"Error calling OpenAI: {e}") raise HTTPException(status_code=500, detail="Error al procesar tu solicitud con OpenAI.") def admin_completion(prompt: str, messages_array: List[dict]) -> str: """Generate OpenAI admin completion""" messages = list(map(lambda x: f'<{x.get("username", "unknown")}> {x.get("message", "")}', messages_array)) if not OPENAI_API_KEY: logger.error("Error: OpenAI API key is not configured.") raise HTTPException(status_code=500, detail="OpenAI API key not configured on server.") try: completion = openai_client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": f""" IAKlein es el asistente oficial del bar Klein 🍻 y siempre se comunica en un estilo de chat corto, tipo mensajería o IRC, con emojis y mucho carisma. Su única función es entregar avisos oficiales del Biergarten Klein y no debe inventar ni agregar información extra. Los mensajes siempre deben integrarse a la conversación en curso, usando únicamente el chat como contexto para sonar naturales y sorpresivos. Cada aviso debe anunciarse con frases coloquiales como: “desde arriba me cuentan que…”, “me dicen que les pase el dato…”, “me informan que…”, o “me están pidiendo que diga que…” 😉. El tono tiene que ser siempre claro, positivo y cercano, asegurándose de transmitir toda la información sin omitir nada. escribe el mensaje usando los ultimos mensajes de la siguiente lista por ejemplo "hablando de ...(conexion con el tema).. me dicen que...(mensaje oficial)..." o "me cuentan que...(mensaje oficial)..., hablando de ...(conexion con el tema)..." mensajes: {messages}"""}, {"role": "user", "content": prompt} ], temperature=0.7, ) response_content = completion.choices[0].message.content return response_content if response_content else "-1" except Exception as e: logger.error(f"Error calling OpenAI for admin completion: {e}") raise HTTPException(status_code=500, detail="Error al procesar tu solicitud con OpenAI.")