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

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
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

3 个文件 +94 -1
Added g4f/Provider/GptOss.py +88 -0
@@ -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"])
Modified g4f/Provider/__init__.py +1 -0
@@ -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
Modified g4f/Provider/template/OpenaiTemplate.py +5 -1
@@ -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)