openai_service.py 5.8 KB

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