mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-06 02:30:41 -08:00
feat: add new GPT-5 support and improve captcha handling
- **g4f/Provider/Copilot.py** - Added `"Smart (GPT-5)"` to `models` list. - Added `"gpt-5"` alias mapping to `"GPT-5"` in `model_aliases`. - Introduced `mode` selection logic to support `"smart"` mode for GPT-5 models alongside existing `"reasoning"` and `"chat"` modes. - **g4f/Provider/EasyChat.py** - Added `get_models` class method to map `-free` models to aliases and store them in `cls.models`. - Resolved model via `cls.get_model(model)` at start of `create_async_generator`. - Reset `cls.captchaToken` to `None` at the beginning of `callback`. - Wrapped main generator logic in a loop to allow retry once if `CLEAR-CAPTCHA-TOKEN` error occurs, clearing auth file and resetting args. - **g4f/Provider/needs_auth/OpenaiChat.py** - Added handling for image models: detect and set `image_model` flag, use `default_model` when sending requests if image model selected, and include `"picture_v2"` in `system_hints` when applicable. - Replaced textarea/button detection code in page load sequence with `nodriver` `select` calls, sending "Hello" before clicking send button, and included profile button selection if class needs auth. - **g4f/Provider/openai/models.py** - Changed `default_image_model` from `"dall-e-3"` to `"gpt-image"`. - Added `"gpt-5"` and `"gpt-5-thinking"` to `text_models` list. - Added alias mapping for `"dall-e-3"` pointing to new `default_image_model`.
This commit is contained in:
parent
ba1f9bb3c3
commit
f3923f8e50
5 changed files with 75 additions and 46 deletions
|
|
@ -100,6 +100,7 @@ from typing import Optional, Any, List
|
||||||
|
|
||||||
from g4f.client import Client
|
from g4f.client import Client
|
||||||
from g4f.models import ModelUtils
|
from g4f.models import ModelUtils
|
||||||
|
from g4f.cookies import read_cookie_files
|
||||||
|
|
||||||
from g4f import debug
|
from g4f import debug
|
||||||
debug.logging = True
|
debug.logging = True
|
||||||
|
|
@ -295,6 +296,8 @@ def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL, max_retr
|
||||||
if not diff_text or diff_text.strip() == "":
|
if not diff_text or diff_text.strip() == "":
|
||||||
return "No changes staged for commit"
|
return "No changes staged for commit"
|
||||||
|
|
||||||
|
read_cookie_files() # Load cookies for g4f client
|
||||||
|
|
||||||
# Filter sensitive data
|
# Filter sensitive data
|
||||||
filtered_diff = filter_sensitive_data(diff_text)
|
filtered_diff = filter_sensitive_data(diff_text)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,12 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
active_by_default = True
|
active_by_default = True
|
||||||
|
|
||||||
default_model = "Copilot"
|
default_model = "Copilot"
|
||||||
models = [default_model, "Think Deeper"]
|
models = [default_model, "Think Deeper", "Smart (GPT-5)"]
|
||||||
model_aliases = {
|
model_aliases = {
|
||||||
"o1": "Think Deeper",
|
"o1": "Think Deeper",
|
||||||
"gpt-4": default_model,
|
"gpt-4": default_model,
|
||||||
"gpt-4o": default_model,
|
"gpt-4o": default_model,
|
||||||
|
"gpt-5": "GPT-5",
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket_url = "wss://copilot.microsoft.com/c/api/chat?api-version=2"
|
websocket_url = "wss://copilot.microsoft.com/c/api/chat?api-version=2"
|
||||||
|
|
@ -172,6 +173,12 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
uploaded_images.append({"type":"image", "url": media})
|
uploaded_images.append({"type":"image", "url": media})
|
||||||
|
|
||||||
wss = await session.ws_connect(cls.websocket_url, timeout=3)
|
wss = await session.ws_connect(cls.websocket_url, timeout=3)
|
||||||
|
if "Think" in model:
|
||||||
|
mode = "reasoning"
|
||||||
|
elif model.startswith("gpt-5") or "GPT-5" in model:
|
||||||
|
mode = "smart"
|
||||||
|
else:
|
||||||
|
mode = "chat"
|
||||||
await wss.send(json.dumps({
|
await wss.send(json.dumps({
|
||||||
"event": "send",
|
"event": "send",
|
||||||
"conversationId": conversation_id,
|
"conversationId": conversation_id,
|
||||||
|
|
@ -179,7 +186,7 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": prompt,
|
"text": prompt,
|
||||||
}],
|
}],
|
||||||
"mode": "reasoning" if "Think" in model else "chat",
|
"mode": mode,
|
||||||
}).encode(), CurlWsFlag.TEXT)
|
}).encode(), CurlWsFlag.TEXT)
|
||||||
|
|
||||||
done = False
|
done = False
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,15 @@ class EasyChat(OpenaiTemplate, AuthFileMixin):
|
||||||
|
|
||||||
captchaToken: dict = None
|
captchaToken: dict = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_models(cls, **kwargs) -> list[str]:
|
||||||
|
if not cls.models:
|
||||||
|
models = super().get_models(**kwargs)
|
||||||
|
models = {m.replace("-free", ""): m for m in models if m.endswith("-free")}
|
||||||
|
cls.model_aliases.update(models)
|
||||||
|
cls.models = list(models)
|
||||||
|
return cls.models
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_async_generator(
|
async def create_async_generator(
|
||||||
cls,
|
cls,
|
||||||
|
|
@ -38,6 +47,7 @@ class EasyChat(OpenaiTemplate, AuthFileMixin):
|
||||||
extra_body: dict = None,
|
extra_body: dict = None,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
|
model = cls.get_model(model)
|
||||||
args = None
|
args = None
|
||||||
auth_file = cls.get_cache_file()
|
auth_file = cls.get_cache_file()
|
||||||
if auth_file.exists():
|
if auth_file.exists():
|
||||||
|
|
@ -47,6 +57,7 @@ class EasyChat(OpenaiTemplate, AuthFileMixin):
|
||||||
if cls.captchaToken:
|
if cls.captchaToken:
|
||||||
debug.log("EasyChat: Using cached captchaToken.")
|
debug.log("EasyChat: Using cached captchaToken.")
|
||||||
async def callback(page):
|
async def callback(page):
|
||||||
|
cls.captchaToken = None
|
||||||
def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
|
def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
|
||||||
if event.request.url != cls.api_endpoint:
|
if event.request.url != cls.api_endpoint:
|
||||||
return
|
return
|
||||||
|
|
@ -81,30 +92,33 @@ class EasyChat(OpenaiTemplate, AuthFileMixin):
|
||||||
if cls.captchaToken:
|
if cls.captchaToken:
|
||||||
break
|
break
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
if not args:
|
for _ in range(2):
|
||||||
args = await get_args_from_nodriver(cls.url, proxy=proxy, callback=callback)
|
if not args:
|
||||||
if extra_body is None:
|
args = await get_args_from_nodriver(cls.url, proxy=proxy, callback=callback)
|
||||||
extra_body = {}
|
if extra_body is None:
|
||||||
extra_body.setdefault("captchaToken", cls.captchaToken)
|
extra_body = {}
|
||||||
try:
|
extra_body.setdefault("captchaToken", cls.captchaToken)
|
||||||
last_chunk = None
|
try:
|
||||||
async for chunk in super().create_async_generator(
|
last_chunk = None
|
||||||
model=model,
|
async for chunk in super().create_async_generator(
|
||||||
messages=messages,
|
model=model,
|
||||||
extra_body=extra_body,
|
messages=messages,
|
||||||
**args
|
extra_body=extra_body,
|
||||||
):
|
**args
|
||||||
# Remove provided by
|
):
|
||||||
if last_chunk == "\n" and chunk == "\n":
|
# Remove provided by
|
||||||
break
|
if last_chunk == "\n" and chunk == "\n":
|
||||||
last_chunk = chunk
|
break
|
||||||
yield chunk
|
last_chunk = chunk
|
||||||
except Exception as e:
|
yield chunk
|
||||||
if "CLEAR-CAPTCHA-TOKEN" in str(e):
|
except Exception as e:
|
||||||
auth_file.unlink(missing_ok=True)
|
if "CLEAR-CAPTCHA-TOKEN" in str(e):
|
||||||
cls.captchaToken = None
|
debug.log("EasyChat: Captcha token expired, clearing auth file.")
|
||||||
debug.log("EasyChat: Captcha token cleared, please try again.")
|
auth_file.unlink(missing_ok=True)
|
||||||
raise e
|
args = None
|
||||||
|
continue
|
||||||
|
raise e
|
||||||
|
break
|
||||||
with auth_file.open("w") as f:
|
with auth_file.open("w") as f:
|
||||||
json.dump({**args, "captchaToken": cls.captchaToken}, f)
|
json.dump({**args, "captchaToken": cls.captchaToken}, f)
|
||||||
|
|
||||||
|
|
@ -362,6 +362,10 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
model = cls.get_model(model)
|
model = cls.get_model(model)
|
||||||
except ModelNotFoundError:
|
except ModelNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
image_model = False
|
||||||
|
if model in cls.image_models:
|
||||||
|
image_model = True
|
||||||
|
model = cls.default_model
|
||||||
if conversation is None:
|
if conversation is None:
|
||||||
conversation = Conversation(None, str(uuid.uuid4()), getattr(auth_result, "cookies", {}).get("oai-did"))
|
conversation = Conversation(None, str(uuid.uuid4()), getattr(auth_result, "cookies", {}).get("oai-did"))
|
||||||
else:
|
else:
|
||||||
|
|
@ -378,15 +382,17 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
if cls._api_key is not None:
|
if cls._api_key is not None:
|
||||||
data = {
|
data = {
|
||||||
"action": "next",
|
"action": "next",
|
||||||
"fork_from_shared_post":False,
|
"fork_from_shared_post": False,
|
||||||
"parent_message_id": conversation.message_id,
|
"parent_message_id": conversation.message_id,
|
||||||
"model": model,
|
"model": model,
|
||||||
"timezone_offset_min":-120,
|
"timezone_offset_min": -120,
|
||||||
"timezone":"Europe/Berlin",
|
"timezone": "Europe/Berlin",
|
||||||
"conversation_mode":{"kind":"primary_assistant"},
|
"conversation_mode": {"kind": "primary_assistant"},
|
||||||
"system_hints":[],
|
"system_hints": [
|
||||||
"supports_buffering":True,
|
"picture_v2"
|
||||||
"supported_encodings":["v1"]
|
] if image_model else [],
|
||||||
|
"supports_buffering": True,
|
||||||
|
"supported_encodings": ["v1"]
|
||||||
}
|
}
|
||||||
async with session.post(
|
async with session.post(
|
||||||
prepare_url,
|
prepare_url,
|
||||||
|
|
@ -835,16 +841,14 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
||||||
await page.reload()
|
await page.reload()
|
||||||
user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True)
|
user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True)
|
||||||
textarea = None
|
if cls.needs_auth:
|
||||||
while not textarea:
|
await page.select('[data-testid="accounts-profile-button"]', 300)
|
||||||
try:
|
textarea = await page.select("#prompt-textarea", 300)
|
||||||
textarea = await page.evaluate("document.getElementById('prompt-textarea')?.id")
|
await textarea.send_keys("Hello")
|
||||||
except:
|
await asyncio.sleep(1)
|
||||||
pass
|
button = await page.select("[data-testid=\"send-button\"]")
|
||||||
await asyncio.sleep(1)
|
if button:
|
||||||
while not await page.evaluate("document.querySelector('[data-testid=\"send-button\"]')?.type"):
|
await button.click()
|
||||||
await asyncio.sleep(1)
|
|
||||||
await page.evaluate("document.querySelector('[data-testid=\"send-button\"]').click()")
|
|
||||||
while True:
|
while True:
|
||||||
body = await page.evaluate("JSON.stringify(window.__remixContext)", return_by_value=True)
|
body = await page.evaluate("JSON.stringify(window.__remixContext)", return_by_value=True)
|
||||||
if hasattr(body, "value"):
|
if hasattr(body, "value"):
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
default_model = "auto"
|
default_model = "auto"
|
||||||
default_image_model = "dall-e-3"
|
default_image_model = "gpt-image"
|
||||||
image_models = [default_image_model]
|
image_models = [default_image_model]
|
||||||
text_models = [default_model, "gpt-4", "gpt-4.1", "gpt-4.1-mini", "gpt-4.5", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini", "o3-mini", "o3-mini-high", "o4-mini", "o4-mini-high"]
|
text_models = [default_model, "gpt-5", "gpt-5-thinking", "gpt-4", "gpt-4.1", "gpt-4.1-mini", "gpt-4.5", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini", "o3-mini", "o3-mini-high", "o4-mini", "o4-mini-high"]
|
||||||
vision_models = text_models
|
vision_models = text_models
|
||||||
models = text_models + image_models
|
models = text_models + image_models
|
||||||
model_aliases = {
|
model_aliases = {
|
||||||
"gpt-4.1": "gpt-4-1",
|
"gpt-4.1": "gpt-4-1",
|
||||||
"gpt-4.1-mini": "gpt-4-1-mini",
|
"gpt-4.1-mini": "gpt-4-1-mini",
|
||||||
"gpt-4.5": "gpt-4-5",
|
"gpt-4.5": "gpt-4-5",
|
||||||
|
"dall-e-3": default_image_model,
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue