response.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import json
  2. from uuid import uuid4
  3. from open_webui.utils.misc import (
  4. openai_chat_chunk_message_template,
  5. openai_chat_completion_message_template,
  6. )
  7. def convert_ollama_tool_call_to_openai(tool_calls: dict) -> dict:
  8. openai_tool_calls = []
  9. for tool_call in tool_calls:
  10. openai_tool_call = {
  11. "index": tool_call.get("index", 0),
  12. "id": tool_call.get("id", f"call_{str(uuid4())}"),
  13. "type": "function",
  14. "function": {
  15. "name": tool_call.get("function", {}).get("name", ""),
  16. "arguments": json.dumps(
  17. tool_call.get("function", {}).get("arguments", {})
  18. ),
  19. },
  20. }
  21. openai_tool_calls.append(openai_tool_call)
  22. return openai_tool_calls
  23. def convert_ollama_usage_to_openai(data: dict) -> dict:
  24. return {
  25. "response_token/s": (
  26. round(
  27. (
  28. (
  29. data.get("eval_count", 0)
  30. / ((data.get("eval_duration", 0) / 10_000_000))
  31. )
  32. * 100
  33. ),
  34. 2,
  35. )
  36. if data.get("eval_duration", 0) > 0
  37. else "N/A"
  38. ),
  39. "prompt_token/s": (
  40. round(
  41. (
  42. (
  43. data.get("prompt_eval_count", 0)
  44. / ((data.get("prompt_eval_duration", 0) / 10_000_000))
  45. )
  46. * 100
  47. ),
  48. 2,
  49. )
  50. if data.get("prompt_eval_duration", 0) > 0
  51. else "N/A"
  52. ),
  53. "total_duration": data.get("total_duration", 0),
  54. "load_duration": data.get("load_duration", 0),
  55. "prompt_eval_count": data.get("prompt_eval_count", 0),
  56. "prompt_tokens": int(data.get("prompt_eval_count", 0)), # This is the OpenAI compatible key
  57. "prompt_eval_duration": data.get("prompt_eval_duration", 0),
  58. "eval_count": data.get("eval_count", 0),
  59. "completion_tokens": int(data.get("eval_count", 0)), # This is the OpenAI compatible key
  60. "eval_duration": data.get("eval_duration", 0),
  61. "approximate_total": (lambda s: f"{s // 3600}h{(s % 3600) // 60}m{s % 60}s")(
  62. (data.get("total_duration", 0) or 0) // 1_000_000_000
  63. ),
  64. "total_tokens": int( # This is the OpenAI compatible key
  65. data.get("prompt_eval_count", 0) + data.get("eval_count", 0)
  66. ),
  67. "completion_tokens_details": { # This is the OpenAI compatible key
  68. "reasoning_tokens": 0,
  69. "accepted_prediction_tokens": 0,
  70. "rejected_prediction_tokens": 0
  71. }
  72. }
  73. def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
  74. model = ollama_response.get("model", "ollama")
  75. message_content = ollama_response.get("message", {}).get("content", "")
  76. tool_calls = ollama_response.get("message", {}).get("tool_calls", None)
  77. openai_tool_calls = None
  78. if tool_calls:
  79. openai_tool_calls = convert_ollama_tool_call_to_openai(tool_calls)
  80. data = ollama_response
  81. usage = convert_ollama_usage_to_openai(data)
  82. response = openai_chat_completion_message_template(
  83. model, message_content, openai_tool_calls, usage
  84. )
  85. return response
  86. async def convert_streaming_response_ollama_to_openai(ollama_streaming_response):
  87. async for data in ollama_streaming_response.body_iterator:
  88. data = json.loads(data)
  89. model = data.get("model", "ollama")
  90. message_content = data.get("message", {}).get("content", "")
  91. tool_calls = data.get("message", {}).get("tool_calls", None)
  92. openai_tool_calls = None
  93. if tool_calls:
  94. openai_tool_calls = convert_ollama_tool_call_to_openai(tool_calls)
  95. done = data.get("done", False)
  96. usage = None
  97. if done:
  98. usage = convert_ollama_usage_to_openai(data)
  99. data = openai_chat_chunk_message_template(
  100. model, message_content if not done else None, openai_tool_calls, usage
  101. )
  102. line = f"data: {json.dumps(data)}\n\n"
  103. yield line
  104. yield "data: [DONE]\n\n"