XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Use new client in inter api

74397096
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

3 个文件 +159 -162
Modified g4f/api/__init__.py +63 -136
@@ -1,21 +1,27 @@
1 import ast
2 1 import logging
3 import time
4 2 import json
5 import random
6 import string
7 3 import uvicorn
8 4 import nest_asyncio
9 5
10 6 from fastapi import FastAPI, Response, Request
11 from fastapi.responses import StreamingResponse
12 from typing import List, Union, Any, Dict, AnyStr
13 #from ._tokenizer import tokenize
7 from fastapi.responses import StreamingResponse, RedirectResponse, HTMLResponse, JSONResponse
8 from pydantic import BaseModel
9 from typing import List
14 10
15 11 import g4f
16 from .. import debug
17
18 debug.logging = True
12 import g4f.debug
13 from g4f.client import Client
14 from g4f.typing import Messages
15
16 class ChatCompletionsConfig(BaseModel):
17 messages: Messages
18 model: str
19 provider: str | None
20 stream: bool = False
21 temperature: float | None
22 max_tokens: int = None
23 stop: list[str] | str | None
24 access_token: str | None
19 25
20 26 class Api:
21 27 def __init__(self, engine: g4f, debug: bool = True, sentry: bool = False,
@@ -25,169 +31,82 @@ class Api:
25 31 self.sentry = sentry
26 32 self.list_ignored_providers = list_ignored_providers
27 33
28 self.app = FastAPI()
34 if debug:
35 g4f.debug.logging = True
36 self.client = Client()
37
29 38 nest_asyncio.apply()
39 self.app = FastAPI()
30 40
31 JSONObject = Dict[AnyStr, Any]
32 JSONArray = List[Any]
33 JSONStructure = Union[JSONArray, JSONObject]
41 self.routes()
34 42
43 def routes(self):
35 44 @self.app.get("/")
36 45 async def read_root():
37 return Response(content=json.dumps({"info": "g4f API"}, indent=4), media_type="application/json")
46 return RedirectResponse("/v1", 302)
38 47
39 48 @self.app.get("/v1")
40 49 async def read_root_v1():
41 return Response(content=json.dumps({"info": "Go to /v1/chat/completions or /v1/models."}, indent=4), media_type="application/json")
50 return HTMLResponse('g4f API: Go to '
51 '<a href="/v1/chat/completions">chat/completions</a> '
52 'or <a href="/v1/models">models</a>.')
42 53
43 54 @self.app.get("/v1/models")
44 55 async def models():
45 model_list = []
46 for model in g4f.Model.__all__():
47 model_info = (g4f.ModelUtils.convert[model])
48 model_list.append({
49 'id': model,
56 model_list = dict(
57 (model, g4f.ModelUtils.convert[model])
58 for model in g4f.Model.__all__()
59 )
60 model_list = [{
61 'id': model_id,
50 62 'object': 'model',
51 63 'created': 0,
52 'owned_by': model_info.base_provider}
53 )
54 return Response(content=json.dumps({
55 'object': 'list',
56 'data': model_list}, indent=4), media_type="application/json")
64 'owned_by': model.base_provider
65 } for model_id, model in model_list.items()]
66 return JSONResponse(model_list)
57 67
58 68 @self.app.get("/v1/models/{model_name}")
59 69 async def model_info(model_name: str):
60 70 try:
61 model_info = (g4f.ModelUtils.convert[model_name])
62
63 return Response(content=json.dumps({
71 model_info = g4f.ModelUtils.convert[model_name]
72 return JSONResponse({
64 73 'id': model_name,
65 74 'object': 'model',
66 75 'created': 0,
67 76 'owned_by': model_info.base_provider
68 }, indent=4), media_type="application/json")
77 })
69 78 except:
70 return Response(content=json.dumps({"error": "The model does not exist."}, indent=4), media_type="application/json")
79 return JSONResponse({"error": "The model does not exist."})
71 80
72 81 @self.app.post("/v1/chat/completions")
73 async def chat_completions(request: Request, item: JSONStructure = None):
74 item_data = {
75 'model': 'gpt-3.5-turbo',
76 'stream': False,
77 }
78
79 # item contains byte keys, and dict.get suppresses error
80 item_data.update({
81 key.decode('utf-8') if isinstance(key, bytes) else key: str(value)
82 for key, value in (item or {}).items()
83 })
84 # messages is str, need dict
85 if isinstance(item_data.get('messages'), str):
86 item_data['messages'] = ast.literal_eval(item_data.get('messages'))
87
88 model = item_data.get('model')
89 stream = True if item_data.get("stream") == "True" else False
90 messages = item_data.get('messages')
91 provider = item_data.get('provider', '').replace('g4f.Provider.', '')
92 provider = provider if provider and provider != "Auto" else None
93 temperature = item_data.get('temperature')
94
82 async def chat_completions(config: ChatCompletionsConfig = None, request: Request = None, provider: str = None):
95 83 try:
96 response = g4f.ChatCompletion.create(
97 model=model,
98 stream=stream,
99 messages=messages,
100 temperature = temperature,
101 provider = provider,
84 config.provider = provider if config.provider is None else config.provider
85 if config.access_token is None and request is not None:
86 auth_header = request.headers.get("Authorization")
87 if auth_header is not None:
88 config.access_token = auth_header.split(None, 1)[-1]
89
90 response = self.client.chat.completions.create(
91 **dict(config),
102 92 ignored=self.list_ignored_providers
103 93 )
104 94 except Exception as e:
105 95 logging.exception(e)
106 content = json.dumps({
107 "error": {"message": f"An error occurred while generating the response:\n{e}"},
108 "model": model,
109 "provider": g4f.get_last_provider(True)
110 })
111 return Response(content=content, status_code=500, media_type="application/json")
112 completion_id = ''.join(random.choices(string.ascii_letters + string.digits, k=28))
113 completion_timestamp = int(time.time())
114
115 if not stream:
116 #prompt_tokens, _ = tokenize(''.join([message['content'] for message in messages]))
117 #completion_tokens, _ = tokenize(response)
118
119 json_data = {
120 'id': f'chatcmpl-{completion_id}',
121 'object': 'chat.completion',
122 'created': completion_timestamp,
123 'model': model,
124 'provider': g4f.get_last_provider(True),
125 'choices': [
126 {
127 'index': 0,
128 'message': {
129 'role': 'assistant',
130 'content': response,
131 },
132 'finish_reason': 'stop',
133 }
134 ],
135 'usage': {
136 'prompt_tokens': 0, #prompt_tokens,
137 'completion_tokens': 0, #completion_tokens,
138 'total_tokens': 0, #prompt_tokens + completion_tokens,
139 },
140 }
141
142 return Response(content=json.dumps(json_data, indent=4), media_type="application/json")
96 return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
97
98 if not config.stream:
99 return JSONResponse(response.to_json())
143 100
144 101 def streaming():
145 102 try:
146 103 for chunk in response:
147 completion_data = {
148 'id': f'chatcmpl-{completion_id}',
149 'object': 'chat.completion.chunk',
150 'created': completion_timestamp,
151 'model': model,
152 'provider': g4f.get_last_provider(True),
153 'choices': [
154 {
155 'index': 0,
156 'delta': {
157 'role': 'assistant',
158 'content': chunk,
159 },
160 'finish_reason': None,
161 }
162 ],
163 }
164 yield f'data: {json.dumps(completion_data)}\n\n'
165 time.sleep(0.03)
166 end_completion_data = {
167 'id': f'chatcmpl-{completion_id}',
168 'object': 'chat.completion.chunk',
169 'created': completion_timestamp,
170 'model': model,
171 'provider': g4f.get_last_provider(True),
172 'choices': [
173 {
174 'index': 0,
175 'delta': {},
176 'finish_reason': 'stop',
177 }
178 ],
179 }
180 yield f'data: {json.dumps(end_completion_data)}\n\n'
104 yield f"data: {json.dumps(chunk.to_json())}\n\n"
181 105 except GeneratorExit:
182 106 pass
183 107 except Exception as e:
184 108 logging.exception(e)
185 content = json.dumps({
186 "error": {"message": f"An error occurred while generating the response:\n{e}"},
187 "model": model,
188 "provider": g4f.get_last_provider(True),
189 })
190 yield f'data: {content}'
109 yield f'data: {format_exception(e, config)}'
191 110
192 111 return StreamingResponse(streaming(), media_type="text/event-stream")
193 112
@@ -198,3 +117,11 @@ class Api:
198 117 def run(self, ip):
199 118 split_ip = ip.split(":")
200 119 uvicorn.run(app=self.app, host=split_ip[0], port=int(split_ip[1]), use_colors=False)
120
121 def format_exception(e: Exception, config: ChatCompletionsConfig) -> str:
122 last_provider = g4f.get_last_provider(True)
123 return json.dumps({
124 "error": {"message": f"ChatCompletionsError: {e.__class__.__name__}: {e}"},
125 "model": last_provider.get("model") if last_provider else config.model,
126 "provider": last_provider.get("name") if last_provider else config.provider
127 })
Modified g4f/client.py +24 -13
@@ -2,6 +2,9 @@ from __future__ import annotations
2 2
3 3 import re
4 4 import os
5 import time
6 import random
7 import string
5 8
6 9 from .stubs import ChatCompletion, ChatCompletionChunk, Image, ImagesResponse
7 10 from .typing import Union, Generator, Messages, ImageType
@@ -10,10 +13,11 @@ from .image import ImageResponse as ImageProviderResponse
10 13 from .Provider.BingCreateImages import BingCreateImages
11 14 from .Provider.needs_auth import Gemini, OpenaiChat
12 15 from .errors import NoImageResponseError
13 from . import get_model_and_provider
16 from . import get_model_and_provider, get_last_provider
14 17
15 18 ImageProvider = Union[BaseProvider, object]
16 19 Proxies = Union[dict, str]
20 IterResponse = Generator[ChatCompletion | ChatCompletionChunk, None, None]
17 21
18 22 def read_json(text: str) -> dict:
19 23 """
@@ -31,18 +35,16 @@ def read_json(text: str) -> dict:
31 35 return text
32 36
33 37 def iter_response(
34 response: iter,
38 response: iter[str],
35 39 stream: bool,
36 40 response_format: dict = None,
37 41 max_tokens: int = None,
38 42 stop: list = None
39 ) -> Generator:
43 ) -> IterResponse:
40 44 content = ""
41 45 finish_reason = None
42 last_chunk = None
46 completion_id = ''.join(random.choices(string.ascii_letters + string.digits, k=28))
43 47 for idx, chunk in enumerate(response):
44 if last_chunk is not None:
45 yield ChatCompletionChunk(last_chunk, finish_reason)
46 48 content += str(chunk)
47 49 if max_tokens is not None and idx + 1 >= max_tokens:
48 50 finish_reason = "length"
@@ -63,16 +65,25 @@ def iter_response(
63 65 if first != -1:
64 66 finish_reason = "stop"
65 67 if stream:
66 last_chunk = chunk
68 yield ChatCompletionChunk(chunk, None, completion_id, int(time.time()))
67 69 if finish_reason is not None:
68 70 break
69 if last_chunk is not None:
70 yield ChatCompletionChunk(last_chunk, finish_reason)
71 if not stream:
71 finish_reason = "stop" if finish_reason is None else finish_reason
72 if stream:
73 yield ChatCompletionChunk(None, finish_reason, completion_id, int(time.time()))
74 else:
72 75 if response_format is not None and "type" in response_format:
73 76 if response_format["type"] == "json_object":
74 77 content = read_json(content)
75 yield ChatCompletion(content, finish_reason)
78 yield ChatCompletion(content, finish_reason, completion_id, int(time.time()))
79
80 def iter_append_model_and_provider(response: IterResponse) -> IterResponse:
81 last_provider = None
82 for chunk in response:
83 last_provider = get_last_provider(True) if last_provider is None else last_provider
84 chunk.model = last_provider.get("model")
85 chunk.provider = last_provider.get("name")
86 yield chunk
76 87
77 88 class Client():
78 89 proxies: Proxies = None
@@ -113,7 +124,7 @@ class Completions():
113 124 stream: bool = False,
114 125 response_format: dict = None,
115 126 max_tokens: int = None,
116 stop: Union[list. str] = None,
127 stop: list[str] | str = None,
117 128 **kwargs
118 129 ) -> Union[ChatCompletion, Generator[ChatCompletionChunk]]:
119 130 if max_tokens is not None:
@@ -128,7 +139,7 @@ class Completions():
128 139 )
129 140 response = provider.create_completion(model, messages, stream=stream, proxy=self.client.get_proxy(), **kwargs)
130 141 stop = [stop] if isinstance(stop, str) else stop
131 response = iter_response(response, stream, response_format, max_tokens, stop)
142 response = iter_append_model_and_provider(iter_response(response, stream, response_format, max_tokens, stop))
132 143 return response if stream else next(response)
133 144
134 145 class Chat():
Modified g4f/stubs.py +72 -13
@@ -2,34 +2,93 @@
2 2 from __future__ import annotations
3 3
4 4 class Model():
5 def __getitem__(self, item):
6 return getattr(self, item)
5 ...
7 6
8 7 class ChatCompletion(Model):
9 def __init__(self, content: str, finish_reason: str):
10 self.choices = [ChatCompletionChoice(ChatCompletionMessage(content, finish_reason))]
8 def __init__(
9 self,
10 content: str,
11 finish_reason: str,
12 completion_id: str = None,
13 created: int = None
14 ):
15 self.id: str = f"chatcmpl-{completion_id}" if completion_id else None
16 self.object: str = "chat.completion"
17 self.created: int = created
18 self.model: str = None
19 self.provider: str = None
20 self.choices = [ChatCompletionChoice(ChatCompletionMessage(content), finish_reason)]
21 self.usage: dict[str, int] = {
22 "prompt_tokens": 0, #prompt_tokens,
23 "completion_tokens": 0, #completion_tokens,
24 "total_tokens": 0, #prompt_tokens + completion_tokens,
25 }
26
27 def to_json(self):
28 return {
29 **self.__dict__,
30 "choices": [choice.to_json() for choice in self.choices]
31 }
11 32
12 33 class ChatCompletionChunk(Model):
13 def __init__(self, content: str, finish_reason: str):
14 self.choices = [ChatCompletionDeltaChoice(ChatCompletionDelta(content, finish_reason))]
34 def __init__(
35 self,
36 content: str,
37 finish_reason: str,
38 completion_id: str = None,
39 created: int = None
40 ):
41 self.id: str = f"chatcmpl-{completion_id}" if completion_id else None
42 self.object: str = "chat.completion.chunk"
43 self.created: int = created
44 self.model: str = None
45 self.provider: str = None
46 self.choices = [ChatCompletionDeltaChoice(ChatCompletionDelta(content), finish_reason)]
47
48 def to_json(self):
49 return {
50 **self.__dict__,
51 "choices": [choice.to_json() for choice in self.choices]
52 }
15 53
16 54 class ChatCompletionMessage(Model):
17 def __init__(self, content: str, finish_reason: str):
55 def __init__(self, content: str | None):
56 self.role = "assistant"
18 57 self.content = content
19 self.finish_reason = finish_reason
58
59 def to_json(self):
60 return self.__dict__
20 61
21 62 class ChatCompletionChoice(Model):
22 def __init__(self, message: ChatCompletionMessage):
63 def __init__(self, message: ChatCompletionMessage, finish_reason: str):
64 self.index = 0
23 65 self.message = message
66 self.finish_reason = finish_reason
67
68 def to_json(self):
69 return {
70 **self.__dict__,
71 "message": self.message.to_json()
72 }
24 73
25 74 class ChatCompletionDelta(Model):
26 def __init__(self, content: str, finish_reason: str):
27 self.content = content
28 self.finish_reason = finish_reason
75 def __init__(self, content: str | None):
76 if content is not None:
77 self.content = content
78
79 def to_json(self):
80 return self.__dict__
29 81
30 82 class ChatCompletionDeltaChoice(Model):
31 def __init__(self, delta: ChatCompletionDelta):
83 def __init__(self, delta: ChatCompletionDelta, finish_reason: str | None):
32 84 self.delta = delta
85 self.finish_reason = finish_reason
86
87 def to_json(self):
88 return {
89 **self.__dict__,
90 "delta": self.delta.to_json()
91 }
33 92
34 93 class Image(Model):
35 94 url: str