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

XFEstudio/gpt4free

Update docs: Using the OpenAI Library Add sse function to requests sessions Small improvments in OpenaiChat and ARTA provider

8f6efd53
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

17 个文件 +291 -86
Modified docs/interference-api.md +21 -11
@@ -8,7 +8,7 @@
8 8 - [From Repository](#from-repository)
9 9 - [Using the Interference API](#using-the-interference-api)
10 10 - [Basic Usage](#basic-usage)
11 - [With OpenAI Library](#with-openai-library)
11 - [Using the OpenAI Library](#using-the-openai-library)
12 12 - [With Requests Library](#with-requests-library)
13 13 - [Selecting a Provider](#selecting-a-provider)
14 14 - [Key Points](#key-points)
@@ -95,35 +95,45 @@ curl -X POST "http://localhost:1337/v1/images/generate" \
95 95 }'
96 96 ```
97 97
98 ---
99
100 ### Using the OpenAI Library
98 101
99 ### With OpenAI Library
102 **To utilize the Inference API with the OpenAI Python library, you can specify the `base_url` to point to your endpoint:**
100 103
101 **You can use the Interference API with the OpenAI Python library by changing the `base_url`:**
102 104 ```python
103 105 from openai import OpenAI
104 106
107 # Initialize the OpenAI client
105 108 client = OpenAI(
106 api_key="secret",
107 base_url="http://localhost:1337/v1"
109 api_key="secret", # Set an API key (use "secret" if your provider doesn't require one)
110 base_url="http://localhost:1337/v1" # Point to your local or custom API endpoint
108 111 )
109 112
113 # Create a chat completion request
110 114 response = client.chat.completions.create(
111 model="gpt-4o-mini",
112 messages=[{"role": "user", "content": "Write a poem about a tree"}],
113 stream=True,
115 model="gpt-4o-mini", # Specify the model to use
116 messages=[{"role": "user", "content": "Write a poem about a tree"}], # Define the input message
117 stream=True, # Enable streaming for real-time responses
114 118 )
115 119
120 # Handle the response
116 121 if isinstance(response, dict):
117 # Not streaming
122 # Non-streaming response
118 123 print(response.choices[0].message.content)
119 124 else:
120 # Streaming
125 # Streaming response
121 126 for token in response:
122 127 content = token.choices[0].delta.content
123 128 if content is not None:
124 129 print(content, end="", flush=True)
125
126 130 ```
131
132 **Notes:**
133 - The `api_key` is required by the OpenAI Python library. If your provider does not require an API key, you can set it to `"secret"`. This value will be ignored by providers in G4F.
134 - Replace `"http://localhost:1337/v1"` with the appropriate URL for your custom or local inference API.
135
136 ---
127 137
128 138
129 139 ### With Requests Library
Modified g4f/Provider/ARTA.py +1 -1
@@ -16,7 +16,7 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
16 16 from .helper import format_image_prompt
17 17
18 18 class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
19 url = "https://img-gen-prod.ai-arta.com"
19 url = "https://ai-arta.com"
20 20 auth_url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
21 21 token_refresh_url = "https://securetoken.googleapis.com/v1/token?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
22 22 image_generation_url = "https://img-gen-prod.ai-arta.com/api/v1/text2image"
Modified g4f/Provider/hf/HuggingFaceAPI.py +11 -11
@@ -92,17 +92,17 @@ class HuggingFaceAPI(OpenaiTemplate):
92 92 model = provider_mapping[provider_key]["providerId"]
93 93 yield ProviderInfo(**{**cls.get_dict(), "label": f"HuggingFace ({provider_key})"})
94 94 break
95 start = calculate_lenght(messages)
96 if start > max_inputs_lenght:
97 if len(messages) > 6:
98 messages = messages[:3] + messages[-3:]
99 if calculate_lenght(messages) > max_inputs_lenght:
100 last_user_message = [{"role": "user", "content": get_last_user_message(messages)}]
101 if len(messages) > 2:
102 messages = [m for m in messages if m["role"] == "system"] + last_user_message
103 if len(messages) > 1 and calculate_lenght(messages) > max_inputs_lenght:
104 messages = last_user_message
105 debug.log(f"Messages trimmed from: {start} to: {calculate_lenght(messages)}")
95 # start = calculate_lenght(messages)
96 # if start > max_inputs_lenght:
97 # if len(messages) > 6:
98 # messages = messages[:3] + messages[-3:]
99 # if calculate_lenght(messages) > max_inputs_lenght:
100 # last_user_message = [{"role": "user", "content": get_last_user_message(messages)}]
101 # if len(messages) > 2:
102 # messages = [m for m in messages if m["role"] == "system"] + last_user_message
103 # if len(messages) > 1 and calculate_lenght(messages) > max_inputs_lenght:
104 # messages = last_user_message
105 # debug.log(f"Messages trimmed from: {start} to: {calculate_lenght(messages)}")
106 106 async for chunk in super().create_async_generator(model, messages, api_base=api_base, api_key=api_key, max_tokens=max_tokens, media=media, **kwargs):
107 107 yield chunk
108 108
Modified g4f/Provider/hf/__init__.py +1 -1
@@ -36,7 +36,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
36 36 messages: Messages,
37 37 **kwargs
38 38 ) -> AsyncResult:
39 if "tools" not in kwargs and "images" not in kwargs and random.random() >= 0.5:
39 if "tools" not in kwargs and "media" not in kwargs and random.random() >= 0.5:
40 40 try:
41 41 is_started = False
42 42 async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
Modified g4f/Provider/needs_auth/OpenaiChat.py +0 -2
@@ -465,8 +465,6 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
465 465 if not line.startswith(b"data: "):
466 466 return
467 467 elif line.startswith(b"data: [DONE]"):
468 if fields.finish_reason is None:
469 fields.finish_reason = "error"
470 468 return
471 469 try:
472 470 line = json.loads(line[6:])
Modified g4f/Provider/template/OpenaiTemplate.py +16 -22
@@ -1,6 +1,5 @@
1 1 from __future__ import annotations
2 2
3 import json
4 3 import requests
5 4
6 5 from ..helper import filter_none, format_image_prompt
@@ -141,7 +140,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
141 140 choice = data["choices"][0]
142 141 if "content" in choice["message"] and choice["message"]["content"]:
143 142 yield choice["message"]["content"].strip()
144 elif "tool_calls" in choice["message"]:
143 if "tool_calls" in choice["message"]:
145 144 yield ToolCalls(choice["message"]["tool_calls"])
146 145 if "usage" in data:
147 146 yield Usage(**data["usage"])
@@ -151,26 +150,21 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
151 150 elif content_type.startswith("text/event-stream"):
152 151 await raise_for_status(response)
153 152 first = True
154 async for line in response.iter_lines():
155 if line.startswith(b"data: "):
156 chunk = line[6:]
157 if chunk == b"[DONE]":
158 break
159 data = json.loads(chunk)
160 cls.raise_error(data)
161 choice = data["choices"][0]
162 if "content" in choice["delta"] and choice["delta"]["content"]:
163 delta = choice["delta"]["content"]
164 if first:
165 delta = delta.lstrip()
166 if delta:
167 first = False
168 yield delta
169 if "usage" in data and data["usage"]:
170 yield Usage(**data["usage"])
171 if "finish_reason" in choice and choice["finish_reason"] is not None:
172 yield FinishReason(choice["finish_reason"])
173 break
153 async for data in response.sse():
154 cls.raise_error(data)
155 choice = data["choices"][0]
156 if "content" in choice["delta"] and choice["delta"]["content"]:
157 delta = choice["delta"]["content"]
158 if first:
159 delta = delta.lstrip()
160 if delta:
161 first = False
162 yield delta
163 if "usage" in data and data["usage"]:
164 yield Usage(**data["usage"])
165 if "finish_reason" in choice and choice["finish_reason"] is not None:
166 yield FinishReason(choice["finish_reason"])
167 break
174 168 else:
175 169 await raise_for_status(response)
176 170 raise ResponseError(f"Not supported content-type: {content_type}")
Modified g4f/api/__init__.py +5 -10
@@ -308,7 +308,8 @@ class Api:
308 308 if credentials is not None and credentials.credentials != "secret":
309 309 config.api_key = credentials.credentials
310 310
311 conversation = return_conversation = None
311 conversation = None
312 return_conversation = config.return_conversation
312 313 if conversation is not None:
313 314 conversation = JsonConversation(**conversation)
314 315 return_conversation = True
@@ -637,11 +638,8 @@ def run_api(
637 638 port: int = None,
638 639 bind: str = None,
639 640 debug: bool = False,
640 workers: int = None,
641 641 use_colors: bool = None,
642 reload: bool = False,
643 ssl_keyfile: str = None,
644 ssl_certfile: str = None
642 **kwargs
645 643 ) -> None:
646 644 print(f'Starting server... [g4f v-{g4f.version.utils.current_version}]' + (" (debug)" if debug else ""))
647 645
@@ -665,10 +663,7 @@ def run_api(
665 663 f"g4f.api:{method}",
666 664 host=host,
667 665 port=int(port),
668 workers=workers,
669 use_colors=use_colors,
670 666 factory=True,
671 reload=reload,
672 ssl_keyfile=ssl_keyfile,
673 ssl_certfile=ssl_certfile
667 use_colors=use_colors,
668 **filter_none(**kwargs)
674 669 )
Modified g4f/api/stubs.py +7 -0
@@ -31,6 +31,7 @@ class ChatCompletionsConfig(BaseModel):
31 31 proxy: Optional[str] = None
32 32 conversation_id: Optional[str] = None
33 33 conversation: Optional[dict] = None
34 return_conversation: Optional[bool] = None
34 35 history_disabled: Optional[bool] = None
35 36 timeout: Optional[int] = None
36 37 tool_calls: list = Field(default=[], examples=[[
@@ -43,6 +44,12 @@ class ChatCompletionsConfig(BaseModel):
43 44 }
44 45 ]])
45 46 tools: list = None
47 parallel_tool_calls: bool = None
48 tool_choice: Optional[str] = None
49 reasoning_effort: Optional[str] = None
50 logit_bias: Optional[dict] = None
51 modalities: Optional[list[str]] = None
52 audio: Optional[dict] = None
46 53 response_format: Optional[dict] = None
47 54
48 55 class ImageGenerationConfig(BaseModel):
Modified g4f/cli.py +3 -1
@@ -32,6 +32,7 @@ def get_api_parser():
32 32
33 33 api_parser.add_argument("--ssl-keyfile", type=str, default=None, help="Path to SSL key file for HTTPS.")
34 34 api_parser.add_argument("--ssl-certfile", type=str, default=None, help="Path to SSL certificate file for HTTPS.")
35 api_parser.add_argument("--log-config", type=str, default=None, help="Custom log config.")
35 36
36 37 return api_parser
37 38
@@ -74,7 +75,8 @@ def run_api_args(args):
74 75 use_colors=not args.disable_colors,
75 76 reload=args.reload,
76 77 ssl_keyfile=args.ssl_keyfile,
77 ssl_certfile=args.ssl_certfile
78 ssl_certfile=args.ssl_certfile,
79 log_config=args.log_config,
78 80 )
79 81
80 82 if __name__ == "__main__":
Modified g4f/client/__init__.py +5 -4
@@ -162,14 +162,15 @@ async def async_iter_response(
162 162 tool_calls = None
163 163 usage = None
164 164 provider: ProviderInfo = None
165 conversation: JsonConversation = None
165 166
166 167 try:
167 168 async for chunk in response:
168 169 if isinstance(chunk, FinishReason):
169 170 finish_reason = chunk.reason
170 171 break
171 elif isinstance(chunk, BaseConversation):
172 yield chunk
172 elif isinstance(chunk, JsonConversation):
173 conversation = chunk
173 174 continue
174 175 elif isinstance(chunk, ToolCalls):
175 176 tool_calls = chunk.get_list()
@@ -228,7 +229,8 @@ async def async_iter_response(
228 229 content, finish_reason, completion_id, int(time.time()), usage=usage,
229 230 **filter_none(
230 231 tool_calls=[ToolCallModel.model_construct(**tool_call) for tool_call in tool_calls]
231 ) if tool_calls is not None else {}
232 ) if tool_calls is not None else {},
233 conversation=None if conversation is None else conversation.get_dict()
232 234 )
233 235 if provider is not None:
234 236 chat_completion.provider = provider.name
@@ -242,7 +244,6 @@ async def async_iter_append_model_and_provider(
242 244 last_model: str,
243 245 last_provider: ProviderType
244 246 ) -> AsyncChatCompletionResponseType:
245 last_provider = None
246 247 try:
247 248 if isinstance(last_provider, BaseRetryProvider):
248 249 async for chunk in response:
Modified g4f/client/stubs.py +4 -2
@@ -132,6 +132,7 @@ class ChatCompletion(BaseModel):
132 132 provider: Optional[str]
133 133 choices: list[ChatCompletionChoice]
134 134 usage: UsageModel
135 conversation: dict
135 136
136 137 @classmethod
137 138 def model_construct(
@@ -141,7 +142,8 @@ class ChatCompletion(BaseModel):
141 142 completion_id: str = None,
142 143 created: int = None,
143 144 tool_calls: list[ToolCallModel] = None,
144 usage: UsageModel = None
145 usage: UsageModel = None,
146 conversation: dict = None
145 147 ):
146 148 return super().model_construct(
147 149 id=f"chatcmpl-{completion_id}" if completion_id else None,
@@ -153,7 +155,7 @@ class ChatCompletion(BaseModel):
153 155 ChatCompletionMessage.model_construct(content, tool_calls),
154 156 finish_reason,
155 157 )],
156 **filter_none(usage=usage)
158 **filter_none(usage=usage, conversation=conversation)
157 159 )
158 160
159 161 class ChatCompletionDelta(BaseModel):
Modified g4f/gui/client/demo.html +4 -2
@@ -298,10 +298,12 @@
298 298
299 299 let oauthResult = localStorage.getItem("oauth");
300 300 if (oauthResult) {
301 let user;
301 302 try {
302 303 oauthResult = JSON.parse(oauthResult);
303 304 user = await hub.whoAmI({accessToken: oauthResult.accessToken});
304 } catch {
305 } catch (e) {
306 console.error(e);
305 307 oauthResult = null;
306 308 localStorage.removeItem("oauth");
307 309 localStorage.removeItem("HuggingFace-api_key");
@@ -365,7 +367,7 @@
365 367 return;
366 368 }
367 369 const lower = data.prompt.toLowerCase();
368 const tags = ["nsfw", "timeline", "feet", "blood", "soap", "orally", "heel", "latex", "bathroom", "boobs", "charts", " text ", "gel", "logo", "infographic", "warts", " bra ", "prostitute", "curvy", "breasts", "written", "bodies", "naked", "classroom", "malone", "dirty", "shoes", "shower", "banner", "fat", "nipples", "couple", "sexual", "sandal", "supplier", "overlord", "succubus", "platinum", "cracy", "crazy", "hemale", "oprah", "lamic", "ropes", "cables", "wires", "dirty", "messy", "cluttered", "chaotic", "disorganized", "disorderly", "untidy", "unorganized", "unorderly", "unsystematic", "disarranged", "disarrayed", "disheveled", "disordered", "jumbled", "muddled", "scattered", "shambolic", "sloppy", "unkept", "unruly"];
370 const tags = ["nsfw", "timeline", "feet", "blood", "soap", "orally", "heel", "latex", "bathroom", "boobs", "charts", "gel", "logo", "infographic", "warts", " bra ", "prostitute", "curvy", "breasts", "written", "bodies", "naked", "classroom", "malone", "dirty", "shoes", "shower", "banner", "fat", "nipples", "couple", "sexual", "sandal", "supplier", "overlord", "succubus", "platinum", "cracy", "crazy", "hemale", "oprah", "lamic", "ropes", "cables", "wires", "dirty", "messy", "cluttered", "chaotic", "disorganized", "disorderly", "untidy", "unorganized", "unorderly", "unsystematic", "disarranged", "disarrayed", "disheveled", "disordered", "jumbled", "muddled", "scattered", "shambolic", "sloppy", "unkept", "unruly"];
369 371 for (i in tags) {
370 372 if (lower.indexOf(tags[i]) != -1) {
371 373 console.log("Skipping image with tag: " + tags[i]);
Added g4f/gui/client/qrcode.html +127 -0
Modified g4f/gui/client/static/js/chat.v1.js +33 -14
Modified g4f/gui/server/backend_api.py +28 -5
Modified g4f/requests/aiohttp.py +13 -0
Modified g4f/requests/curl_cffi.py +12 -0