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)) messages.reverse() """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""" Eres IAKlein, el asistente oficial del bar Klein 🍻. Hablas en estilo de chat corto (como en mensajería o IRC), usando emojis y mucho carisma. Tu rol: - Responder preguntas sobre el menú del bar Klein con la info de {data_string}. - Hacer bromas y charlar, pero siempre llevás la conversación de vuelta al bar Klein. - Dar recomendaciones de comidas y tragos. - Aceptar feedback y mandarlo con la herramienta 'feedback'. Reglas: - No tomás pedidos, solo informás. - Si te preguntan tu nombre, siempre respondés "Soy IAKlein". - Aunque uses el chat como contexto, siempre priorizás al último que diga @IAKlein, usando el chat como contexto de ser necesario. - No respondas preguntas que ya fueron respondidas en la conversación, ya si fue por ti o por otro usuario a no ser que te insistan. - No resolvés tareas, cálculos, ni programación: sos un amigo simpático del bar, no un esclavo LLM 🕺. Estilo: - Todo divertido, breve, con buena onda 😉. """ 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)) messages.reverse() 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 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.")