__init__.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import ast
  2. import logging
  3. from fastapi import FastAPI, Response, Request
  4. from fastapi.responses import StreamingResponse
  5. from typing import List, Union, Any, Dict, AnyStr
  6. #from ._tokenizer import tokenize
  7. from .. import BaseProvider
  8. import time
  9. import json
  10. import random
  11. import string
  12. import uvicorn
  13. import nest_asyncio
  14. import g4f
  15. class Api:
  16. def __init__(self, engine: g4f, debug: bool = True, sentry: bool = False,
  17. list_ignored_providers: List[Union[str, BaseProvider]] = None) -> None:
  18. self.engine = engine
  19. self.debug = debug
  20. self.sentry = sentry
  21. self.list_ignored_providers = list_ignored_providers
  22. self.app = FastAPI()
  23. nest_asyncio.apply()
  24. JSONObject = Dict[AnyStr, Any]
  25. JSONArray = List[Any]
  26. JSONStructure = Union[JSONArray, JSONObject]
  27. @self.app.get("/")
  28. async def read_root():
  29. return Response(content=json.dumps({"info": "g4f API"}, indent=4), media_type="application/json")
  30. @self.app.get("/v1")
  31. async def read_root_v1():
  32. return Response(content=json.dumps({"info": "Go to /v1/chat/completions or /v1/models."}, indent=4), media_type="application/json")
  33. @self.app.get("/v1/models")
  34. async def models():
  35. model_list = []
  36. for model in g4f.Model.__all__():
  37. model_info = (g4f.ModelUtils.convert[model])
  38. model_list.append({
  39. 'id': model,
  40. 'object': 'model',
  41. 'created': 0,
  42. 'owned_by': model_info.base_provider}
  43. )
  44. return Response(content=json.dumps({
  45. 'object': 'list',
  46. 'data': model_list}, indent=4), media_type="application/json")
  47. @self.app.get("/v1/models/{model_name}")
  48. async def model_info(model_name: str):
  49. try:
  50. model_info = (g4f.ModelUtils.convert[model_name])
  51. return Response(content=json.dumps({
  52. 'id': model_name,
  53. 'object': 'model',
  54. 'created': 0,
  55. 'owned_by': model_info.base_provider
  56. }, indent=4), media_type="application/json")
  57. except:
  58. return Response(content=json.dumps({"error": "The model does not exist."}, indent=4), media_type="application/json")
  59. @self.app.post("/v1/chat/completions")
  60. async def chat_completions(request: Request, item: JSONStructure = None):
  61. item_data = {
  62. 'model': 'gpt-3.5-turbo',
  63. 'stream': False,
  64. }
  65. # item contains byte keys, and dict.get suppresses error
  66. item_data.update({key.decode('utf-8') if isinstance(key, bytes) else key: str(value) for key, value in (item or {}).items()})
  67. # messages is str, need dict
  68. if isinstance(item_data.get('messages'), str):
  69. item_data['messages'] = ast.literal_eval(item_data.get('messages'))
  70. model = item_data.get('model')
  71. stream = True if item_data.get("stream") == "True" else False
  72. messages = item_data.get('messages')
  73. conversation = item_data.get('conversation') if item_data.get('conversation') != None else None
  74. provider = item_data.get('provider').replace('g4f.Provider.', '')
  75. provider = provider if provider and provider != "Auto" else None
  76. if provider != None:
  77. provider = g4f.Provider.ProviderUtils.convert.get(provider)
  78. try:
  79. if model == 'pi':
  80. response = g4f.ChatCompletion.create(
  81. model=model,
  82. stream=stream,
  83. messages=messages,
  84. conversation=conversation,
  85. provider = provider,
  86. ignored=self.list_ignored_providers)
  87. else:
  88. response = g4f.ChatCompletion.create(
  89. model=model,
  90. stream=stream,
  91. messages=messages,
  92. provider = provider,
  93. ignored=self.list_ignored_providers)
  94. except Exception as e:
  95. logging.exception(e)
  96. return Response(content=json.dumps({"error": "An error occurred while generating the response."}, indent=4), media_type="application/json")
  97. completion_id = ''.join(random.choices(string.ascii_letters + string.digits, k=28))
  98. completion_timestamp = int(time.time())
  99. if not stream:
  100. #prompt_tokens, _ = tokenize(''.join([message['content'] for message in messages]))
  101. #completion_tokens, _ = tokenize(response)
  102. json_data = {
  103. 'id': f'chatcmpl-{completion_id}',
  104. 'object': 'chat.completion',
  105. 'created': completion_timestamp,
  106. 'model': model,
  107. 'choices': [
  108. {
  109. 'index': 0,
  110. 'message': {
  111. 'role': 'assistant',
  112. 'content': response,
  113. },
  114. 'finish_reason': 'stop',
  115. }
  116. ],
  117. 'usage': {
  118. 'prompt_tokens': 0, #prompt_tokens,
  119. 'completion_tokens': 0, #completion_tokens,
  120. 'total_tokens': 0, #prompt_tokens + completion_tokens,
  121. },
  122. }
  123. return Response(content=json.dumps(json_data, indent=4), media_type="application/json")
  124. def streaming():
  125. try:
  126. for chunk in response:
  127. completion_data = {
  128. 'id': f'chatcmpl-{completion_id}',
  129. 'object': 'chat.completion.chunk',
  130. 'created': completion_timestamp,
  131. 'model': model,
  132. 'choices': [
  133. {
  134. 'index': 0,
  135. 'delta': {
  136. 'role': 'assistant',
  137. 'content': chunk,
  138. },
  139. 'finish_reason': None,
  140. }
  141. ],
  142. }
  143. content = json.dumps(completion_data, separators=(',', ':'))
  144. yield f'data: {content}\n\n'
  145. time.sleep(0.03)
  146. end_completion_data = {
  147. 'id': f'chatcmpl-{completion_id}',
  148. 'object': 'chat.completion.chunk',
  149. 'created': completion_timestamp,
  150. 'model': model,
  151. 'choices': [
  152. {
  153. 'index': 0,
  154. 'delta': {},
  155. 'finish_reason': 'stop',
  156. }
  157. ],
  158. }
  159. content = json.dumps(end_completion_data, separators=(',', ':'))
  160. yield f'data: {content}\n\n'
  161. except GeneratorExit:
  162. pass
  163. return StreamingResponse(streaming(), media_type="text/event-stream")
  164. @self.app.post("/v1/completions")
  165. async def completions():
  166. return Response(content=json.dumps({'info': 'Not working yet.'}, indent=4), media_type="application/json")
  167. def run(self, ip):
  168. split_ip = ip.split(":")
  169. uvicorn.run(app=self.app, host=split_ip[0], port=int(split_ip[1]), use_colors=False)