返回提交历史
Added
g4f/Provider/GptOss.py
+88
-0
Modified
g4f/Provider/__init__.py
+1
-0
Modified
g4f/Provider/template/OpenaiTemplate.py
+5
-1
XFEstudio/gpt4free
feat: add GptOss provider and reasoning handling in OpenaiTemplate
- Added new provider `GptOss` in `g4f/Provider/GptOss.py` with support for async message generation via SSE - Registered `GptOss` in `g4f/Provider/__init__.py` - Implemented logic in `GptOss.create_async_generator` to handle both new and existing conversations with SSE streaming response handling - Handled event types including `thread.created`, `thread.item_updated`, and `thread.updated` within `GptOss` - Modified `read_response` in `OpenaiTemplate.py` to yield `Reasoning` objects using `reasoning_content` or fallback to `reasoning` from `choice["delta"]
bf285b56
代码差异
3 个文件
+94
-1
@@ -0,0 +1,88 @@
1
from __future__ import annotations
2
3
4
from ..typing import AsyncResult, Messages
5
from ..providers.response import JsonConversation, Reasoning, TitleGeneration
6
from ..requests import StreamSession, raise_for_status
7
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
8
from .helper import get_last_user_message
9
10
class GptOss(AsyncGeneratorProvider, ProviderModelMixin):
11
label = "gpt-oss (playground)"
12
url = "https://gpt-oss.com"
13
api_endpoint = "https://api.gpt-oss.com/chatkit"
14
working = True
15
active_by_default = True
16
17
default_model = "gpt-oss-120b"
18
models = [default_model, "gpt-oss-20b"]
19
20
@classmethod
21
async def create_async_generator(
22
cls,
23
model: str,
24
messages: Messages,
25
conversation: JsonConversation = None,
26
reasoning_effort: str = "high",
27
proxy: str = None,
28
**kwargs
29
) -> AsyncResult:
30
if not model:
31
model = cls.default_model
32
user_message = get_last_user_message(messages)
33
cookies = {}
34
if conversation is None:
35
data = {
36
"op": "threads.create",
37
"params": {
38
"input": {
39
"text": user_message,
40
"content": [{"type": "input_text", "text": user_message}],
41
"quoted_text": "",
42
"attachments": []
43
}
44
}
45
}
46
else:
47
data = {
48
"op":"threads.addMessage",
49
"params": {
50
"input": {
51
"text": user_message,
52
"content": [{"type": "input_text", "text": user_message}],
53
"quoted_text": "",
54
"attachments": []
55
},
56
"threadId": conversation.id
57
}
58
}
59
cookies["user_id"] = conversation.user_id
60
headers = {
61
"accept": "text/event-stream",
62
"x-reasoning-effort": reasoning_effort,
63
"x-selected-model": model,
64
"x-show-reasoning": "true"
65
}
66
async with StreamSession(
67
headers=headers,
68
cookies=cookies,
69
proxy=proxy,
70
) as session:
71
async with session.post(
72
cls.api_endpoint,
73
json=data
74
) as response:
75
await raise_for_status(response)
76
async for chunk in response.sse():
77
if chunk.get("type") == "thread.created":
78
yield JsonConversation(id=chunk["thread"]["id"], user_id=response.cookies.get("user_id"))
79
elif chunk.get("type") == "thread.item_updated":
80
entry = chunk.get("update", {}).get("entry", chunk.get("update", {}))
81
if entry.get("type") == "thought":
82
yield Reasoning(entry.get("content"))
83
elif entry.get("type") == "recap":
84
pass #yield Reasoning(status=entry.get("summary"))
85
elif entry.get("type") == "assistant_message.content_part.text_delta":
86
yield entry.get("delta")
87
elif chunk.get("type") == "thread.updated":
88
yield TitleGeneration(chunk["thread"]["title"])
@@ -40,6 +40,7 @@ from .Copilot import Copilot
40
40
from .DeepInfraChat import DeepInfraChat
41
41
from .DuckDuckGo import DuckDuckGo
42
42
from .Free2GPT import Free2GPT
43
from .GptOss import GptOss
43
44
from .ImageLabs import ImageLabs
44
45
from .Kimi import Kimi
45
46
from .LambdaChat import LambdaChat
@@ -165,6 +165,10 @@ async def read_response(response: StreamResponse, stream: bool, prompt: str, pro
165
165
yield message["content"].strip()
166
166
if "tool_calls" in message:
167
167
yield ToolCalls(message["tool_calls"])
168
if choice:
169
reasoning_content = choice.get("delta", {}).get("reasoning_content", choice.get("delta", {}).get("reasoning"))
170
if reasoning_content:
171
yield Reasoning(reasoning_content, status="")
168
172
audio = message.get("audio", {})
169
173
if "data" in audio:
170
174
if download_media:
@@ -201,7 +205,7 @@ async def read_response(response: StreamResponse, stream: bool, prompt: str, pro
201
205
tool_calls = choice.get("delta", {}).get("tool_calls")
202
206
if tool_calls:
203
207
yield ToolCalls(choice["delta"]["tool_calls"])
204
reasoning_content = choice.get("delta", {}).get("reasoning_content")
208
reasoning_content = choice.get("delta", {}).get("reasoning_content", choice.get("delta", {}).get("reasoning"))
205
209
if reasoning_content:
206
210
reasoning = True
207
211
yield Reasoning(reasoning_content)