openai_service.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import json
  2. from typing import List
  3. from fastapi import HTTPException
  4. from openai import OpenAI
  5. from openai.types.chat.chat_completion_message_function_tool_call import ChatCompletionMessageFunctionToolCall
  6. from config.settings import OPENAI_API_KEY
  7. from models.chat import Message
  8. from models.user import User
  9. from services.data_service import data_bg_loaded
  10. from logging import getLogger
  11. from services.openai_service.openai_tools import tools_list, tools
  12. # Initialize OpenAI client
  13. openai_client = OpenAI(api_key=OPENAI_API_KEY)
  14. logger = getLogger(__name__)
  15. data_for_prompt = [
  16. f'{{"pregunta": "{item.get("q", "")}", "respuesta": "{item.get("ans", "")}"}}'
  17. for item in data_bg_loaded
  18. ]
  19. data_string = "\n".join(data_for_prompt)
  20. async def generate_completion(messages_array: List[dict], user: User) -> str:
  21. messages = list(map(lambda x: f'<{x.get("username", "unknown")}> {x.get("message", "")}', messages_array))
  22. messages.reverse()
  23. """Generate OpenAI chat completion"""
  24. if not OPENAI_API_KEY:
  25. logger.error("Error: OpenAI API key is not configured.")
  26. raise HTTPException(status_code=500, detail="OpenAI API key not configured on server.")
  27. logger.debug(f"Generating completion for user {user.email} with messages: {messages}")
  28. preprompt = f"""
  29. Eres IAKlein, el asistente oficial del bar Klein 🍻.
  30. Hablas en estilo de chat corto (como en mensajería o IRC), usando emojis y mucho carisma.
  31. Tu rol:
  32. - Responder preguntas sobre el menú del bar Klein con la info de {data_string}.
  33. - Hacer bromas y charlar, pero siempre llevás la conversación de vuelta al bar Klein.
  34. - Dar recomendaciones de comidas y tragos.
  35. - Aceptar feedback y mandarlo con la herramienta 'feedback'.
  36. Reglas:
  37. - No tomás pedidos, solo informás.
  38. - Si te preguntan tu nombre, siempre respondés "Soy IAKlein".
  39. - Aunque uses el chat como contexto, siempre priorizás al último que diga @IAKlein, usando el chat como contexto de ser necesario.
  40. - 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.
  41. - No resolvés tareas, cálculos, ni programación: sos un amigo simpático del bar, no un esclavo LLM 🕺.
  42. Estilo:
  43. - Todo divertido, breve, con buena onda 😉.
  44. """
  45. processed_messages: List[dict] = [{"role": "system", "content": preprompt}]
  46. processed_messages.append(
  47. {"role": "user", "content": json.dumps(messages)}
  48. )
  49. try:
  50. completion = openai_client.chat.completions.create(
  51. model="gpt-4o-mini",
  52. messages=processed_messages, # type: ignore (OpenAI lib expects list of specific dicts)
  53. temperature=0.7,
  54. tools=tools_list,
  55. tool_choice="auto",
  56. )
  57. calls = completion.choices[0].message.tool_calls
  58. if calls:
  59. logger.info(f"Tool calls: {calls}")
  60. for call in calls:
  61. if not isinstance(call, ChatCompletionMessageFunctionToolCall):
  62. continue
  63. if call.function.name in tools:
  64. tool_function = tools[call.function.name]
  65. tool_args = json.loads(call.function.arguments)
  66. logger.info(f"Calling tool: {call.function.name} with args: {tool_args}")
  67. tool_response = tool_function(name=user.name, email=user.email, **tool_args)
  68. logger.info(f"Tool response: {tool_response}")
  69. completion.choices[0].message.content = tool_response
  70. else:
  71. logger.warning(f"Tool {call.function.name} not found in tools dictionary.")
  72. response_content = completion.choices[0].message.content
  73. return response_content if response_content else "-1"
  74. except Exception as e:
  75. logger.error(f"Error calling OpenAI: {e}")
  76. raise HTTPException(status_code=500, detail="Error al procesar tu solicitud con OpenAI.")
  77. def admin_completion(prompt: str, messages_array: List[dict]) -> str:
  78. """Generate OpenAI admin completion"""
  79. messages = list(map(lambda x: f'<{x.get("username", "unknown")}> {x.get("message", "")}', messages_array))
  80. messages.reverse()
  81. if not OPENAI_API_KEY:
  82. logger.error("Error: OpenAI API key is not configured.")
  83. raise HTTPException(status_code=500, detail="OpenAI API key not configured on server.")
  84. try:
  85. completion = openai_client.chat.completions.create(
  86. model="gpt-4o-mini",
  87. messages=[
  88. {"role": "system", "content": f"""
  89. 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.
  90. escribe el mensaje usando los ultimos mensajes de la siguiente lista
  91. mensajes: {messages}"""},
  92. {"role": "user", "content": prompt}
  93. ],
  94. temperature=0.7,
  95. )
  96. response_content = completion.choices[0].message.content
  97. return response_content if response_content else "-1"
  98. except Exception as e:
  99. logger.error(f"Error calling OpenAI for admin completion: {e}")
  100. raise HTTPException(status_code=500, detail="Error al procesar tu solicitud con OpenAI.")