mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-23 02:20:49 -08:00
Merge pull request #2920 from hlohaus/30Mar
feat: add LM Arena provider, async‑ify Copilot & surface follow‑up su…
This commit is contained in:
commit
0c0c72c203
29 changed files with 630 additions and 320 deletions
|
|
@ -26,6 +26,10 @@ from typing import Optional, Dict, Any, List, Tuple
|
||||||
|
|
||||||
from g4f.client import Client
|
from g4f.client import Client
|
||||||
from g4f.models import ModelUtils
|
from g4f.models import ModelUtils
|
||||||
|
import g4f.Provider
|
||||||
|
|
||||||
|
from g4f import debug
|
||||||
|
debug.logging = True
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
DEFAULT_MODEL = "claude-3.7-sonnet"
|
DEFAULT_MODEL = "claude-3.7-sonnet"
|
||||||
|
|
@ -184,16 +188,21 @@ def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL) -> Optio
|
||||||
|
|
||||||
# Make API call
|
# Make API call
|
||||||
response = client.chat.completions.create(
|
response = client.chat.completions.create(
|
||||||
|
prompt,
|
||||||
model=model,
|
model=model,
|
||||||
messages=[{"role": "user", "content": prompt}]
|
stream=True,
|
||||||
)
|
)
|
||||||
|
content = []
|
||||||
|
for chunk in response:
|
||||||
# Stop spinner and clear line
|
# Stop spinner and clear line
|
||||||
|
if spinner:
|
||||||
spinner.set()
|
spinner.set()
|
||||||
sys.stdout.write("\r" + " " * 50 + "\r")
|
print(" " * 50 + "\n", flush=True)
|
||||||
sys.stdout.flush()
|
spinner = None
|
||||||
|
if isinstance(chunk.choices[0].delta.content, str):
|
||||||
return response.choices[0].message.content.strip()
|
content.append(chunk.choices[0].delta.content)
|
||||||
|
print(chunk.choices[0].delta.content, end="", flush=True)
|
||||||
|
return "".join(content).strip()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Stop spinner if it's running
|
# Stop spinner if it's running
|
||||||
if 'spinner' in locals() and spinner:
|
if 'spinner' in locals() and spinner:
|
||||||
|
|
@ -306,11 +315,6 @@ def main():
|
||||||
print("Failed to generate commit message after multiple attempts.")
|
print("Failed to generate commit message after multiple attempts.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("\nGenerated commit message:")
|
|
||||||
print("-" * 50)
|
|
||||||
print(commit_message)
|
|
||||||
print("-" * 50)
|
|
||||||
|
|
||||||
if args.edit:
|
if args.edit:
|
||||||
print("\nOpening editor to modify commit message...")
|
print("\nOpening editor to modify commit message...")
|
||||||
commit_message = edit_commit_message(commit_message)
|
commit_message = edit_commit_message(commit_message)
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ from datetime import datetime, timedelta
|
||||||
from ..typing import AsyncResult, Messages, MediaListType
|
from ..typing import AsyncResult, Messages, MediaListType
|
||||||
from ..requests.raise_for_status import raise_for_status
|
from ..requests.raise_for_status import raise_for_status
|
||||||
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
||||||
|
from .openai.har_file import get_har_files
|
||||||
from ..image import to_data_uri
|
from ..image import to_data_uri
|
||||||
from ..cookies import get_cookies_dir
|
from ..cookies import get_cookies_dir
|
||||||
from .helper import format_image_prompt
|
from .helper import format_image_prompt, render_messages
|
||||||
from ..providers.response import JsonConversation, ImageResponse
|
from ..providers.response import JsonConversation, ImageResponse
|
||||||
from ..tools.media import merge_media
|
from ..tools.media import merge_media
|
||||||
from ..errors import PaymentRequiredError
|
from ..errors import RateLimitError
|
||||||
from .. import debug
|
from .. import debug
|
||||||
|
|
||||||
class Conversation(JsonConversation):
|
class Conversation(JsonConversation):
|
||||||
|
|
@ -428,15 +429,9 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
Optional[dict]: Session data if found, None otherwise
|
Optional[dict]: Session data if found, None otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
har_dir = get_cookies_dir()
|
for file in get_har_files():
|
||||||
if not os.access(har_dir, os.R_OK):
|
|
||||||
return None
|
|
||||||
|
|
||||||
for root, _, files in os.walk(har_dir):
|
|
||||||
for file in files:
|
|
||||||
if file.endswith(".har"):
|
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(root, file), 'rb') as f:
|
with open(file, 'rb') as f:
|
||||||
har_data = json.load(f)
|
har_data = json.load(f)
|
||||||
|
|
||||||
for entry in har_data['log']['entries']:
|
for entry in har_data['log']['entries']:
|
||||||
|
|
@ -451,7 +446,6 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
'"user"' in content['text'] and
|
'"user"' in content['text'] and
|
||||||
'"email"' in content['text'] and
|
'"email"' in content['text'] and
|
||||||
'"expires"' in content['text']):
|
'"expires"' in content['text']):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Remove any HTML or other non-JSON content
|
# Remove any HTML or other non-JSON content
|
||||||
text = content['text'].strip()
|
text = content['text'].strip()
|
||||||
|
|
@ -573,7 +567,7 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
conversation.message_history = []
|
conversation.message_history = []
|
||||||
|
|
||||||
current_messages = []
|
current_messages = []
|
||||||
for i, msg in enumerate(messages):
|
for i, msg in enumerate(render_messages(messages)):
|
||||||
msg_id = conversation.chat_id if i == 0 and msg["role"] == "user" else cls.generate_id()
|
msg_id = conversation.chat_id if i == 0 and msg["role"] == "user" else cls.generate_id()
|
||||||
current_msg = {
|
current_msg = {
|
||||||
"id": msg_id,
|
"id": msg_id,
|
||||||
|
|
@ -690,8 +684,8 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
async for chunk in response.content.iter_any():
|
async for chunk in response.content.iter_any():
|
||||||
if chunk:
|
if chunk:
|
||||||
chunk_text = chunk.decode()
|
chunk_text = chunk.decode()
|
||||||
if chunk_text == "You have reached your request limit for the hour":
|
if "You have reached your request limit for the hour" in chunk_text:
|
||||||
raise PaymentRequiredError(chunk_text)
|
raise RateLimitError(chunk_text)
|
||||||
full_response.append(chunk_text)
|
full_response.append(chunk_text)
|
||||||
# Only yield chunks for non-image models
|
# Only yield chunks for non-image models
|
||||||
if model != cls.default_image_model:
|
if model != cls.default_image_model:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from ..requests import Session, StreamSession, get_args_from_nodriver, raise_for
|
||||||
from ..requests import DEFAULT_HEADERS, has_nodriver, has_curl_cffi
|
from ..requests import DEFAULT_HEADERS, has_nodriver, has_curl_cffi
|
||||||
from ..providers.response import FinishReason, Usage
|
from ..providers.response import FinishReason, Usage
|
||||||
from ..errors import ResponseStatusError, ModelNotFoundError
|
from ..errors import ResponseStatusError, ModelNotFoundError
|
||||||
from .helper import to_string
|
from .helper import render_messages
|
||||||
|
|
||||||
class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
|
class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
|
||||||
label = "Cloudflare AI"
|
label = "Cloudflare AI"
|
||||||
|
|
@ -82,7 +82,7 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
|
||||||
elif has_nodriver:
|
elif has_nodriver:
|
||||||
cls._args = await get_args_from_nodriver(cls.url, proxy, timeout, cookies)
|
cls._args = await get_args_from_nodriver(cls.url, proxy, timeout, cookies)
|
||||||
else:
|
else:
|
||||||
cls._args = {"headers": DEFAULT_HEADERS, "cookies": {}}
|
cls._args = {"headers": DEFAULT_HEADERS, "cookies": {}, "impersonate": "chrome"}
|
||||||
try:
|
try:
|
||||||
model = cls.get_model(model)
|
model = cls.get_model(model)
|
||||||
except ModelNotFoundError:
|
except ModelNotFoundError:
|
||||||
|
|
@ -90,8 +90,7 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
|
||||||
data = {
|
data = {
|
||||||
"messages": [{
|
"messages": [{
|
||||||
**message,
|
**message,
|
||||||
"content": to_string(message["content"]),
|
"parts": [{"type":"text", "text": message["content"]}]} for message in render_messages(messages)],
|
||||||
"parts": [{"type":"text", "text": to_string(message["content"])}]} for message in messages],
|
|
||||||
"lora": None,
|
"lora": None,
|
||||||
"model": model,
|
"model": model,
|
||||||
"max_tokens": max_tokens,
|
"max_tokens": max_tokens,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@ from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from curl_cffi.requests import Session
|
from curl_cffi.requests import AsyncSession
|
||||||
from curl_cffi import CurlWsFlag
|
from curl_cffi import CurlWsFlag
|
||||||
has_curl_cffi = True
|
has_curl_cffi = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -18,14 +17,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_nodriver = False
|
has_nodriver = False
|
||||||
|
|
||||||
from .base_provider import AbstractProvider, ProviderModelMixin
|
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
||||||
from .helper import format_prompt_max_length
|
from .helper import format_prompt_max_length
|
||||||
from .openai.har_file import get_headers, get_har_files
|
from .openai.har_file import get_headers, get_har_files
|
||||||
from ..typing import CreateResult, Messages, MediaListType
|
from ..typing import AsyncResult, Messages, MediaListType
|
||||||
from ..errors import MissingRequirementsError, NoValidHarFileError, MissingAuthError
|
from ..errors import MissingRequirementsError, NoValidHarFileError, MissingAuthError
|
||||||
from ..requests.raise_for_status import raise_for_status
|
from ..providers.response import BaseConversation, JsonConversation, RequestLogin, ImageResponse, FinishReason, SuggestedFollowups, TitleGeneration, Sources, SourceLink
|
||||||
from ..providers.response import BaseConversation, JsonConversation, RequestLogin, ImageResponse, FinishReason, SuggestedFollowups
|
|
||||||
from ..providers.asyncio import get_running_loop
|
|
||||||
from ..tools.media import merge_media
|
from ..tools.media import merge_media
|
||||||
from ..requests import get_nodriver
|
from ..requests import get_nodriver
|
||||||
from ..image import to_bytes, is_accepted_format
|
from ..image import to_bytes, is_accepted_format
|
||||||
|
|
@ -38,7 +35,7 @@ class Conversation(JsonConversation):
|
||||||
def __init__(self, conversation_id: str):
|
def __init__(self, conversation_id: str):
|
||||||
self.conversation_id = conversation_id
|
self.conversation_id = conversation_id
|
||||||
|
|
||||||
class Copilot(AbstractProvider, ProviderModelMixin):
|
class Copilot(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
label = "Microsoft Copilot"
|
label = "Microsoft Copilot"
|
||||||
url = "https://copilot.microsoft.com"
|
url = "https://copilot.microsoft.com"
|
||||||
|
|
||||||
|
|
@ -62,20 +59,20 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
_cookies: dict = None
|
_cookies: dict = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_completion(
|
async def create_async_generator(
|
||||||
cls,
|
cls,
|
||||||
model: str,
|
model: str,
|
||||||
messages: Messages,
|
messages: Messages,
|
||||||
stream: bool = False,
|
stream: bool = False,
|
||||||
proxy: str = None,
|
proxy: str = None,
|
||||||
timeout: int = 900,
|
timeout: int = 30,
|
||||||
prompt: str = None,
|
prompt: str = None,
|
||||||
media: MediaListType = None,
|
media: MediaListType = None,
|
||||||
conversation: BaseConversation = None,
|
conversation: BaseConversation = None,
|
||||||
return_conversation: bool = False,
|
return_conversation: bool = False,
|
||||||
api_key: str = None,
|
api_key: str = None,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> CreateResult:
|
) -> AsyncResult:
|
||||||
if not has_curl_cffi:
|
if not has_curl_cffi:
|
||||||
raise MissingRequirementsError('Install or update "curl_cffi" package | pip install -U curl_cffi')
|
raise MissingRequirementsError('Install or update "curl_cffi" package | pip install -U curl_cffi')
|
||||||
model = cls.get_model(model)
|
model = cls.get_model(model)
|
||||||
|
|
@ -91,14 +88,13 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
debug.log(f"Copilot: {h}")
|
debug.log(f"Copilot: {h}")
|
||||||
if has_nodriver:
|
if has_nodriver:
|
||||||
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
||||||
get_running_loop(check_nested=True)
|
cls._access_token, cls._cookies = await get_access_token_and_cookies(cls.url, proxy)
|
||||||
cls._access_token, cls._cookies = asyncio.run(get_access_token_and_cookies(cls.url, proxy))
|
|
||||||
else:
|
else:
|
||||||
raise h
|
raise h
|
||||||
websocket_url = f"{websocket_url}&accessToken={quote(cls._access_token)}"
|
websocket_url = f"{websocket_url}&accessToken={quote(cls._access_token)}"
|
||||||
headers = {"authorization": f"Bearer {cls._access_token}"}
|
headers = {"authorization": f"Bearer {cls._access_token}"}
|
||||||
|
|
||||||
with Session(
|
async with AsyncSession(
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
impersonate="chrome",
|
impersonate="chrome",
|
||||||
|
|
@ -107,35 +103,19 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
) as session:
|
) as session:
|
||||||
if cls._access_token is not None:
|
if cls._access_token is not None:
|
||||||
cls._cookies = session.cookies.jar if hasattr(session.cookies, "jar") else session.cookies
|
cls._cookies = session.cookies.jar if hasattr(session.cookies, "jar") else session.cookies
|
||||||
# if cls._access_token is None:
|
response = await session.get("https://copilot.microsoft.com/c/api/user")
|
||||||
# try:
|
|
||||||
# url = "https://copilot.microsoft.com/cl/eus-sc/collect"
|
|
||||||
# headers = {
|
|
||||||
# "Accept": "application/x-clarity-gzip",
|
|
||||||
# "referrer": "https://copilot.microsoft.com/onboarding"
|
|
||||||
# }
|
|
||||||
# response = session.post(url, headers=headers, data=get_clarity())
|
|
||||||
# clarity_token = json.loads(response.text.split(" ", maxsplit=1)[-1])[0]["value"]
|
|
||||||
# debug.log(f"Copilot: Clarity Token: ...{clarity_token[-12:]}")
|
|
||||||
# except Exception as e:
|
|
||||||
# debug.log(f"Copilot: {e}")
|
|
||||||
# else:
|
|
||||||
# clarity_token = None
|
|
||||||
response = session.get("https://copilot.microsoft.com/c/api/user")
|
|
||||||
if response.status_code == 401:
|
if response.status_code == 401:
|
||||||
raise MissingAuthError("Status 401: Invalid access token")
|
raise MissingAuthError("Status 401: Invalid access token")
|
||||||
raise_for_status(response)
|
response.raise_for_status()
|
||||||
user = response.json().get('firstName')
|
user = response.json().get('firstName')
|
||||||
if user is None:
|
if user is None:
|
||||||
cls._access_token = None
|
cls._access_token = None
|
||||||
debug.log(f"Copilot: User: {user or 'null'}")
|
debug.log(f"Copilot: User: {user or 'null'}")
|
||||||
if conversation is None:
|
if conversation is None:
|
||||||
response = session.post(cls.conversation_url)
|
response = await session.post(cls.conversation_url)
|
||||||
raise_for_status(response)
|
response.raise_for_status()
|
||||||
conversation_id = response.json().get("id")
|
conversation_id = response.json().get("id")
|
||||||
conversation = Conversation(conversation_id)
|
conversation = Conversation(conversation_id)
|
||||||
if return_conversation:
|
|
||||||
yield conversation
|
|
||||||
if prompt is None:
|
if prompt is None:
|
||||||
prompt = format_prompt_max_length(messages, 10000)
|
prompt = format_prompt_max_length(messages, 10000)
|
||||||
debug.log(f"Copilot: Created conversation: {conversation_id}")
|
debug.log(f"Copilot: Created conversation: {conversation_id}")
|
||||||
|
|
@ -144,30 +124,28 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
if prompt is None:
|
if prompt is None:
|
||||||
prompt = get_last_user_message(messages)
|
prompt = get_last_user_message(messages)
|
||||||
debug.log(f"Copilot: Use conversation: {conversation_id}")
|
debug.log(f"Copilot: Use conversation: {conversation_id}")
|
||||||
|
if return_conversation:
|
||||||
|
yield conversation
|
||||||
|
|
||||||
uploaded_images = []
|
uploaded_images = []
|
||||||
media, _ = [(None, None), *merge_media(media, messages)].pop()
|
for media, _ in merge_media(media, messages):
|
||||||
if media:
|
|
||||||
if not isinstance(media, str):
|
if not isinstance(media, str):
|
||||||
data = to_bytes(media)
|
data = to_bytes(media)
|
||||||
response = session.post(
|
response = await session.post(
|
||||||
"https://copilot.microsoft.com/c/api/attachments",
|
"https://copilot.microsoft.com/c/api/attachments",
|
||||||
headers={"content-type": is_accepted_format(data)},
|
headers={
|
||||||
|
"content-type": is_accepted_format(data),
|
||||||
|
"content-length": str(len(data)),
|
||||||
|
},
|
||||||
data=data
|
data=data
|
||||||
)
|
)
|
||||||
raise_for_status(response)
|
response.raise_for_status()
|
||||||
media = response.json().get("url")
|
media = response.json().get("url")
|
||||||
uploaded_images.append({"type":"image", "url": media})
|
uploaded_images.append({"type":"image", "url": media})
|
||||||
|
|
||||||
wss = session.ws_connect(cls.websocket_url)
|
wss = await session.ws_connect(cls.websocket_url, timeout=3)
|
||||||
# if clarity_token is not None:
|
await wss.send(json.dumps({"event":"setOptions","supportedCards":["weather","local","image","sports","video","ads","finance"],"ads":{"supportedTypes":["multimedia","product","tourActivity","propertyPromotion","text"]}}));
|
||||||
# wss.send(json.dumps({
|
await wss.send(json.dumps({
|
||||||
# "event": "challengeResponse",
|
|
||||||
# "token": clarity_token,
|
|
||||||
# "method":"clarity"
|
|
||||||
# }).encode(), CurlWsFlag.TEXT)
|
|
||||||
wss.send(json.dumps({"event":"setOptions","supportedCards":["weather","local","image","sports","video","ads","finance"],"ads":{"supportedTypes":["multimedia","product","tourActivity","propertyPromotion","text"]}}));
|
|
||||||
wss.send(json.dumps({
|
|
||||||
"event": "send",
|
"event": "send",
|
||||||
"conversationId": conversation_id,
|
"conversationId": conversation_id,
|
||||||
"content": [*uploaded_images, {
|
"content": [*uploaded_images, {
|
||||||
|
|
@ -177,20 +155,20 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
"mode": "reasoning" if "Think" in model else "chat",
|
"mode": "reasoning" if "Think" in model else "chat",
|
||||||
}).encode(), CurlWsFlag.TEXT)
|
}).encode(), CurlWsFlag.TEXT)
|
||||||
|
|
||||||
is_started = False
|
done = False
|
||||||
msg = None
|
msg = None
|
||||||
image_prompt: str = None
|
image_prompt: str = None
|
||||||
last_msg = None
|
last_msg = None
|
||||||
|
sources = {}
|
||||||
try:
|
try:
|
||||||
while True:
|
while not wss.closed:
|
||||||
try:
|
try:
|
||||||
msg = wss.recv()[0]
|
msg = await asyncio.wait_for(wss.recv(), 3 if done else timeout)
|
||||||
msg = json.loads(msg)
|
msg = json.loads(msg[0])
|
||||||
except:
|
except:
|
||||||
break
|
break
|
||||||
last_msg = msg
|
last_msg = msg
|
||||||
if msg.get("event") == "appendText":
|
if msg.get("event") == "appendText":
|
||||||
is_started = True
|
|
||||||
yield msg.get("text")
|
yield msg.get("text")
|
||||||
elif msg.get("event") == "generatingImage":
|
elif msg.get("event") == "generatingImage":
|
||||||
image_prompt = msg.get("prompt")
|
image_prompt = msg.get("prompt")
|
||||||
|
|
@ -198,20 +176,28 @@ class Copilot(AbstractProvider, ProviderModelMixin):
|
||||||
yield ImageResponse(msg.get("url"), image_prompt, {"preview": msg.get("thumbnailUrl")})
|
yield ImageResponse(msg.get("url"), image_prompt, {"preview": msg.get("thumbnailUrl")})
|
||||||
elif msg.get("event") == "done":
|
elif msg.get("event") == "done":
|
||||||
yield FinishReason("stop")
|
yield FinishReason("stop")
|
||||||
break
|
done = True
|
||||||
elif msg.get("event") == "suggestedFollowups":
|
elif msg.get("event") == "suggestedFollowups":
|
||||||
yield SuggestedFollowups(msg.get("suggestions"))
|
yield SuggestedFollowups(msg.get("suggestions"))
|
||||||
break
|
break
|
||||||
elif msg.get("event") == "replaceText":
|
elif msg.get("event") == "replaceText":
|
||||||
yield msg.get("text")
|
yield msg.get("text")
|
||||||
|
elif msg.get("event") == "titleUpdate":
|
||||||
|
yield TitleGeneration(msg.get("title"))
|
||||||
|
elif msg.get("event") == "citation":
|
||||||
|
sources[msg.get("url")] = msg
|
||||||
|
yield SourceLink(list(sources.keys()).index(msg.get("url")), msg.get("url"))
|
||||||
elif msg.get("event") == "error":
|
elif msg.get("event") == "error":
|
||||||
raise RuntimeError(f"Error: {msg}")
|
raise RuntimeError(f"Error: {msg}")
|
||||||
elif msg.get("event") not in ["received", "startMessage", "citation", "partCompleted"]:
|
elif msg.get("event") not in ["received", "startMessage", "partCompleted"]:
|
||||||
debug.log(f"Copilot Message: {msg}")
|
debug.log(f"Copilot Message: {msg}")
|
||||||
if not is_started:
|
if not done:
|
||||||
raise RuntimeError(f"Invalid response: {last_msg}")
|
raise RuntimeError(f"Invalid response: {last_msg}")
|
||||||
|
if sources:
|
||||||
|
yield Sources(sources.values())
|
||||||
finally:
|
finally:
|
||||||
wss.close()
|
if not wss.closed:
|
||||||
|
await wss.close()
|
||||||
|
|
||||||
async def get_access_token_and_cookies(url: str, proxy: str = None, target: str = "ChatAI",):
|
async def get_access_token_and_cookies(url: str, proxy: str = None, target: str = "ChatAI",):
|
||||||
browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
|
browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
|
||||||
|
|
@ -264,8 +250,3 @@ def readHAR(url: str):
|
||||||
raise NoValidHarFileError("No access token found in .har files")
|
raise NoValidHarFileError("No access token found in .har files")
|
||||||
|
|
||||||
return api_key, cookies
|
return api_key, cookies
|
||||||
|
|
||||||
def get_clarity() -> bytes:
|
|
||||||
#{"e":["0.7.58",5,7284,4779,"n59ae4ieqq","aln5en","1upufhz",1,0,0],"a":[[7323,12,65,217,324],[7344,12,65,214,329],[7385,12,65,211,334],[7407,12,65,210,337],[7428,12,65,209,338],[7461,12,65,209,339],[7497,12,65,209,339],[7531,12,65,208,340],[7545,12,65,208,342],[11654,13,65,208,342],[11728,14,65,208,342],[11728,9,65,208,342,17535,19455,0,0,0,"Annehmen",null,"52w7wqv1r.8ovjfyrpu",1],[7284,4,1,393,968,393,968,0,0,231,310,939,0],[12063,0,2,147,3,4,4,18,5,1,10,79,25,15],[12063,36,6,[11938,0]]]}
|
|
||||||
body = base64.b64decode("H4sIAAAAAAAAA23RwU7DMAwG4HfJ2aqS2E5ibjxH1cMOnQYqYZvUTQPx7vyJRGGAemj01XWcP+9udg+j80MetDhSyrEISc5GrqrtZnmaTydHbrdUnSsWYT2u+8Obo0Ce/IQvaDBmjkwhUlKKIRNHmQgosqEArWPRDQMx90rxeUMPzB1j+UJvwNIxhTvsPcXyX1T+rizE4juK3mEEhpAUg/JvzW1/+U/tB1LATmhqotoiweMea50PLy2vui4LOY3XfD1dwnkor5fn/e18XBFgm6fHjSzZmCyV7d3aRByAEYextaTHEH3i5pgKGVP/s+DScE5PuLKIpW6FnCi1gY3Rbpqmj0/DI/+L7QEAAA==")
|
|
||||||
return body
|
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,21 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from duckduckgo_search import DDGS
|
from duckai import DuckAI
|
||||||
from duckduckgo_search.exceptions import DuckDuckGoSearchException, RatelimitException, ConversationLimitException
|
|
||||||
has_requirements = True
|
has_requirements = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_requirements = False
|
has_requirements = False
|
||||||
try:
|
|
||||||
import nodriver
|
|
||||||
has_nodriver = True
|
|
||||||
except ImportError:
|
|
||||||
has_nodriver = False
|
|
||||||
|
|
||||||
from ..typing import AsyncResult, Messages
|
from ..typing import CreateResult, Messages
|
||||||
from ..requests import get_nodriver
|
from .base_provider import AbstractProvider, ProviderModelMixin
|
||||||
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
|
||||||
from .helper import get_last_user_message
|
from .helper import get_last_user_message
|
||||||
|
|
||||||
class DuckDuckGo(AsyncGeneratorProvider, ProviderModelMixin):
|
class DuckDuckGo(AbstractProvider, ProviderModelMixin):
|
||||||
label = "Duck.ai (duckduckgo_search)"
|
label = "Duck.ai (duckduckgo_search)"
|
||||||
url = "https://duckduckgo.com/aichat"
|
url = "https://duckduckgo.com/aichat"
|
||||||
api_base = "https://duckduckgo.com/duckchat/v1/"
|
api_base = "https://duckduckgo.com/duckchat/v1/"
|
||||||
|
|
||||||
working = False
|
working = has_requirements
|
||||||
supports_stream = True
|
supports_stream = True
|
||||||
supports_system_message = True
|
supports_system_message = True
|
||||||
supports_message_history = True
|
supports_message_history = True
|
||||||
|
|
@ -32,7 +25,7 @@ class DuckDuckGo(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
default_model = "gpt-4o-mini"
|
default_model = "gpt-4o-mini"
|
||||||
models = [default_model, "meta-llama/Llama-3.3-70B-Instruct-Turbo", "claude-3-haiku-20240307", "o3-mini", "mistralai/Mistral-Small-24B-Instruct-2501"]
|
models = [default_model, "meta-llama/Llama-3.3-70B-Instruct-Turbo", "claude-3-haiku-20240307", "o3-mini", "mistralai/Mistral-Small-24B-Instruct-2501"]
|
||||||
|
|
||||||
ddgs: DDGS = None
|
duck_ai: DuckAI = None
|
||||||
|
|
||||||
model_aliases = {
|
model_aliases = {
|
||||||
"gpt-4": "gpt-4o-mini",
|
"gpt-4": "gpt-4o-mini",
|
||||||
|
|
@ -42,44 +35,17 @@ class DuckDuckGo(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_async_generator(
|
def create_completion(
|
||||||
cls,
|
cls,
|
||||||
model: str,
|
model: str,
|
||||||
messages: Messages,
|
messages: Messages,
|
||||||
proxy: str = None,
|
proxy: str = None,
|
||||||
timeout: int = 60,
|
timeout: int = 60,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> AsyncResult:
|
) -> CreateResult:
|
||||||
if not has_requirements:
|
if not has_requirements:
|
||||||
raise ImportError("duckduckgo_search is not installed. Install it with `pip install duckduckgo-search`.")
|
raise ImportError("duckai is not installed. Install it with `pip install -U duckai`.")
|
||||||
if cls.ddgs is None:
|
if cls.duck_ai is None:
|
||||||
cls.ddgs = DDGS(proxy=proxy, timeout=timeout)
|
cls.duck_ai = DuckAI(proxy=proxy, timeout=timeout)
|
||||||
if has_nodriver:
|
|
||||||
await cls.nodriver_auth(proxy=proxy)
|
|
||||||
model = cls.get_model(model)
|
model = cls.get_model(model)
|
||||||
for chunk in cls.ddgs.chat_yield(get_last_user_message(messages), model, timeout):
|
yield cls.duck_ai.chat(get_last_user_message(messages), model, timeout)
|
||||||
yield chunk
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def nodriver_auth(cls, proxy: str = None):
|
|
||||||
browser, stop_browser = await get_nodriver(proxy=proxy)
|
|
||||||
try:
|
|
||||||
page = browser.main_tab
|
|
||||||
def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
|
|
||||||
if cls.api_base in event.request.url:
|
|
||||||
if "X-Vqd-4" in event.request.headers:
|
|
||||||
cls.ddgs._chat_vqd = event.request.headers["X-Vqd-4"]
|
|
||||||
if "X-Vqd-Hash-1" in event.request.headers:
|
|
||||||
cls.ddgs._chat_vqd_hash = event.request.headers["X-Vqd-Hash-1"]
|
|
||||||
if "F-Fe-Version" in event.request.headers:
|
|
||||||
cls.ddgs._chat_xfe = event.request.headers["F-Fe-Version" ]
|
|
||||||
await page.send(nodriver.cdp.network.enable())
|
|
||||||
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
|
||||||
page = await browser.get(cls.url)
|
|
||||||
while True:
|
|
||||||
if cls.ddgs._chat_vqd:
|
|
||||||
break
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
await page.close()
|
|
||||||
finally:
|
|
||||||
stop_browser()
|
|
||||||
|
|
@ -6,4 +6,4 @@ class FreeRouter(OpenaiTemplate):
|
||||||
label = "CablyAI FreeRouter"
|
label = "CablyAI FreeRouter"
|
||||||
url = "https://freerouter.cablyai.com"
|
url = "https://freerouter.cablyai.com"
|
||||||
api_base = "https://freerouter.cablyai.com/v1"
|
api_base = "https://freerouter.cablyai.com/v1"
|
||||||
working = False
|
working = True
|
||||||
|
|
@ -52,8 +52,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
audio_models = [default_audio_model]
|
audio_models = [default_audio_model]
|
||||||
extra_image_models = ["flux-pro", "flux-dev", "flux-schnell", "midjourney", "dall-e-3", "turbo"]
|
extra_image_models = ["flux-pro", "flux-dev", "flux-schnell", "midjourney", "dall-e-3", "turbo"]
|
||||||
vision_models = [default_vision_model, "gpt-4o-mini", "openai", "openai-large", "searchgpt"]
|
vision_models = [default_vision_model, "gpt-4o-mini", "openai", "openai-large", "searchgpt"]
|
||||||
extra_text_models = vision_models
|
|
||||||
_models_loaded = False
|
_models_loaded = False
|
||||||
|
# https://github.com/pollinations/pollinations/blob/master/text.pollinations.ai/generateTextPortkey.js#L15
|
||||||
model_aliases = {
|
model_aliases = {
|
||||||
### Text Models ###
|
### Text Models ###
|
||||||
"gpt-4o-mini": "openai",
|
"gpt-4o-mini": "openai",
|
||||||
|
|
@ -100,42 +100,31 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
|
|
||||||
cls.image_models = all_image_models
|
cls.image_models = all_image_models
|
||||||
|
|
||||||
# Update of text models
|
|
||||||
text_response = requests.get("https://text.pollinations.ai/models")
|
text_response = requests.get("https://text.pollinations.ai/models")
|
||||||
text_response.raise_for_status()
|
text_response.raise_for_status()
|
||||||
models = text_response.json()
|
models = text_response.json()
|
||||||
|
|
||||||
# Purpose of text models
|
|
||||||
cls.text_models = [
|
|
||||||
model.get("name")
|
|
||||||
for model in models
|
|
||||||
if "input_modalities" in model and "text" in model["input_modalities"]
|
|
||||||
]
|
|
||||||
|
|
||||||
# Purpose of audio models
|
# Purpose of audio models
|
||||||
cls.audio_models = {
|
cls.audio_models = {
|
||||||
model.get("name"): model.get("voices")
|
model.get("name"): model.get("voices")
|
||||||
for model in models
|
for model in models
|
||||||
if model.get("audio")
|
if "output_modalities" in model and "audio" in model["output_modalities"]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create a set of unique text models starting with default model
|
# Create a set of unique text models starting with default model
|
||||||
unique_text_models = {cls.default_model}
|
unique_text_models = cls.text_models.copy()
|
||||||
|
|
||||||
# Add models from vision_models
|
# Add models from vision_models
|
||||||
unique_text_models.update(cls.vision_models)
|
unique_text_models.extend(cls.vision_models)
|
||||||
|
|
||||||
# Add models from the API response
|
# Add models from the API response
|
||||||
for model in models:
|
for model in models:
|
||||||
model_name = model.get("name")
|
model_name = model.get("name")
|
||||||
if model_name and "input_modalities" in model and "text" in model["input_modalities"]:
|
if model_name and "input_modalities" in model and "text" in model["input_modalities"]:
|
||||||
unique_text_models.add(model_name)
|
unique_text_models.append(model_name)
|
||||||
|
|
||||||
# Convert to list and update text_models
|
# Convert to list and update text_models
|
||||||
cls.text_models = list(unique_text_models)
|
cls.text_models = list(dict.fromkeys(unique_text_models))
|
||||||
|
|
||||||
# Update extra_text_models with unique vision models
|
|
||||||
cls.extra_text_models = [model for model in cls.vision_models if model != cls.default_model]
|
|
||||||
|
|
||||||
cls._models_loaded = True
|
cls._models_loaded = True
|
||||||
|
|
||||||
|
|
@ -148,12 +137,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
debug.error(f"Failed to fetch models: {e}")
|
debug.error(f"Failed to fetch models: {e}")
|
||||||
|
|
||||||
# Return unique models across all categories
|
# Return unique models across all categories
|
||||||
all_models = set(cls.text_models)
|
all_models = cls.text_models.copy()
|
||||||
all_models.update(cls.image_models)
|
all_models.extend(cls.image_models)
|
||||||
all_models.update(cls.audio_models.keys())
|
all_models.extend(cls.audio_models.keys())
|
||||||
result = list(all_models)
|
return list(dict.fromkeys(all_models))
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_async_generator(
|
async def create_async_generator(
|
||||||
|
|
@ -265,15 +252,15 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
|
query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
|
||||||
prompt = quote_plus(prompt)[:2048-256-len(query)]
|
prompt = quote_plus(prompt)[:2048-256-len(query)]
|
||||||
url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
|
url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
|
||||||
def get_image_url(i: int = 0, seed: Optional[int] = None):
|
def get_image_url(i: int, seed: Optional[int] = None):
|
||||||
if i == 0:
|
if i == 1:
|
||||||
if not cache and seed is None:
|
if not cache and seed is None:
|
||||||
seed = random.randint(0, 2**32)
|
seed = random.randint(0, 2**32)
|
||||||
else:
|
else:
|
||||||
seed = random.randint(0, 2**32)
|
seed = random.randint(0, 2**32)
|
||||||
return f"{url}&seed={seed}" if seed else url
|
return f"{url}&seed={seed}" if seed else url
|
||||||
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
|
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
|
||||||
async def get_image(i: int = 0, seed: Optional[int] = None):
|
async def get_image(i: int, seed: Optional[int] = None):
|
||||||
async with session.get(get_image_url(i, seed), allow_redirects=False) as response:
|
async with session.get(get_image_url(i, seed), allow_redirects=False) as response:
|
||||||
try:
|
try:
|
||||||
await raise_for_status(response)
|
await raise_for_status(response)
|
||||||
|
|
@ -343,6 +330,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
if line[6:].startswith(b"[DONE]"):
|
if line[6:].startswith(b"[DONE]"):
|
||||||
break
|
break
|
||||||
result = json.loads(line[6:])
|
result = json.loads(line[6:])
|
||||||
|
if "error" in result:
|
||||||
|
raise ResponseError(result["error"].get("message", result["error"]))
|
||||||
if "usage" in result:
|
if "usage" in result:
|
||||||
yield Usage(**result["usage"])
|
yield Usage(**result["usage"])
|
||||||
choices = result.get("choices", [{}])
|
choices = result.get("choices", [{}])
|
||||||
|
|
|
||||||
253
g4f/Provider/hf_space/LMArenaProvider.py
Normal file
253
g4f/Provider/hf_space/LMArenaProvider.py
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from ...typing import AsyncResult, Messages
|
||||||
|
from ...requests import StreamSession, raise_for_status
|
||||||
|
from ...providers.response import FinishReason
|
||||||
|
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin
|
||||||
|
from ..helper import format_prompt
|
||||||
|
from ... import debug
|
||||||
|
|
||||||
|
class LMArenaProvider(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
|
||||||
|
label = "LM Arena"
|
||||||
|
url = "https://lmarena.ai"
|
||||||
|
api_endpoint = "/queue/join?"
|
||||||
|
|
||||||
|
working = True
|
||||||
|
|
||||||
|
default_model = "chatgpt-4o-latest-20250326"
|
||||||
|
model_aliases = {"gpt-4o": default_model}
|
||||||
|
models = [
|
||||||
|
default_model,
|
||||||
|
"gpt-4.1-2025-04-14",
|
||||||
|
"gemini-2.5-pro-exp-03-25",
|
||||||
|
"llama-4-maverick-03-26-experimental",
|
||||||
|
"grok-3-preview-02-24",
|
||||||
|
"claude-3-7-sonnet-20250219",
|
||||||
|
"claude-3-7-sonnet-20250219-thinking-32k",
|
||||||
|
"deepseek-v3-0324",
|
||||||
|
"llama-4-maverick-17b-128e-instruct",
|
||||||
|
"gpt-4.1-mini-2025-04-14",
|
||||||
|
"gpt-4.1-nano-2025-04-14",
|
||||||
|
"gemini-2.0-flash-thinking-exp-01-21",
|
||||||
|
"gemini-2.0-flash-001",
|
||||||
|
"gemini-2.0-flash-lite-preview-02-05",
|
||||||
|
"gemma-3-27b-it",
|
||||||
|
"gemma-3-12b-it",
|
||||||
|
"gemma-3-4b-it",
|
||||||
|
"deepseek-r1",
|
||||||
|
"claude-3-5-sonnet-20241022",
|
||||||
|
"o3-mini",
|
||||||
|
"llama-3.3-70b-instruct",
|
||||||
|
"gpt-4o-mini-2024-07-18",
|
||||||
|
"gpt-4o-2024-11-20",
|
||||||
|
"gpt-4o-2024-08-06",
|
||||||
|
"gpt-4o-2024-05-13",
|
||||||
|
"command-a-03-2025",
|
||||||
|
"qwq-32b",
|
||||||
|
"p2l-router-7b",
|
||||||
|
"claude-3-5-haiku-20241022",
|
||||||
|
"claude-3-5-sonnet-20240620",
|
||||||
|
"doubao-1.5-pro-32k-250115",
|
||||||
|
"doubao-1.5-vision-pro-32k-250115",
|
||||||
|
"mistral-small-24b-instruct-2501",
|
||||||
|
"phi-4",
|
||||||
|
"amazon-nova-pro-v1.0",
|
||||||
|
"amazon-nova-lite-v1.0",
|
||||||
|
"amazon-nova-micro-v1.0",
|
||||||
|
"cobalt-exp-beta-v3",
|
||||||
|
"cobalt-exp-beta-v4",
|
||||||
|
"qwen-max-2025-01-25",
|
||||||
|
"qwen-plus-0125-exp",
|
||||||
|
"qwen2.5-vl-32b-instruct",
|
||||||
|
"qwen2.5-vl-72b-instruct",
|
||||||
|
"gemini-1.5-pro-002",
|
||||||
|
"gemini-1.5-flash-002",
|
||||||
|
"gemini-1.5-flash-8b-001",
|
||||||
|
"gemini-1.5-pro-001",
|
||||||
|
"gemini-1.5-flash-001",
|
||||||
|
"llama-3.1-405b-instruct-bf16",
|
||||||
|
"llama-3.3-nemotron-49b-super-v1",
|
||||||
|
"llama-3.1-nemotron-ultra-253b-v1",
|
||||||
|
"llama-3.1-nemotron-70b-instruct",
|
||||||
|
"llama-3.1-70b-instruct",
|
||||||
|
"llama-3.1-8b-instruct",
|
||||||
|
"hunyuan-standard-2025-02-10",
|
||||||
|
"hunyuan-large-2025-02-10",
|
||||||
|
"hunyuan-standard-vision-2024-12-31",
|
||||||
|
"hunyuan-turbo-0110",
|
||||||
|
"hunyuan-turbos-20250226",
|
||||||
|
"mistral-large-2411",
|
||||||
|
"pixtral-large-2411",
|
||||||
|
"mistral-large-2407",
|
||||||
|
"llama-3.1-nemotron-51b-instruct",
|
||||||
|
"granite-3.1-8b-instruct",
|
||||||
|
"granite-3.1-2b-instruct",
|
||||||
|
"step-2-16k-exp-202412",
|
||||||
|
"step-2-16k-202502",
|
||||||
|
"step-1o-vision-32k-highres",
|
||||||
|
"yi-lightning",
|
||||||
|
"glm-4-plus",
|
||||||
|
"glm-4-plus-0111",
|
||||||
|
"jamba-1.5-large",
|
||||||
|
"jamba-1.5-mini",
|
||||||
|
"gemma-2-27b-it",
|
||||||
|
"gemma-2-9b-it",
|
||||||
|
"gemma-2-2b-it",
|
||||||
|
"eureka-chatbot",
|
||||||
|
"claude-3-haiku-20240307",
|
||||||
|
"claude-3-sonnet-20240229",
|
||||||
|
"claude-3-opus-20240229",
|
||||||
|
"nemotron-4-340b",
|
||||||
|
"llama-3-70b-instruct",
|
||||||
|
"llama-3-8b-instruct",
|
||||||
|
"qwen2.5-plus-1127",
|
||||||
|
"qwen2.5-coder-32b-instruct",
|
||||||
|
"qwen2.5-72b-instruct",
|
||||||
|
"qwen-max-0919",
|
||||||
|
"qwen-vl-max-1119",
|
||||||
|
"qwen-vl-max-0809",
|
||||||
|
"llama-3.1-tulu-3-70b",
|
||||||
|
"olmo-2-0325-32b-instruct",
|
||||||
|
"gpt-3.5-turbo-0125",
|
||||||
|
"reka-core-20240904",
|
||||||
|
"reka-flash-20240904",
|
||||||
|
"c4ai-aya-expanse-32b",
|
||||||
|
"c4ai-aya-expanse-8b",
|
||||||
|
"c4ai-aya-vision-32b",
|
||||||
|
"command-r-plus-08-2024",
|
||||||
|
"command-r-08-2024",
|
||||||
|
"codestral-2405",
|
||||||
|
"mixtral-8x22b-instruct-v0.1",
|
||||||
|
"mixtral-8x7b-instruct-v0.1",
|
||||||
|
"pixtral-12b-2409",
|
||||||
|
"ministral-8b-2410"]
|
||||||
|
|
||||||
|
_args: dict = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _random_session_hash():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_payloads(cls, model_id: str, session_hash: str, messages: Messages, max_tokens: int, temperature: float, top_p: float):
|
||||||
|
first_payload = {
|
||||||
|
"data": [
|
||||||
|
None,
|
||||||
|
model_id,
|
||||||
|
{"text": format_prompt(messages), "files": []},
|
||||||
|
{
|
||||||
|
"text_models": [model_id],
|
||||||
|
"all_text_models": [model_id],
|
||||||
|
"vision_models": [],
|
||||||
|
"all_vision_models": [],
|
||||||
|
"image_gen_models": [],
|
||||||
|
"all_image_gen_models": [],
|
||||||
|
"search_models": [],
|
||||||
|
"all_search_models": [],
|
||||||
|
"models": [model_id],
|
||||||
|
"all_models": [model_id],
|
||||||
|
"arena_type": "text-arena"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"event_data": None,
|
||||||
|
"fn_index": 117,
|
||||||
|
"trigger_id": 159,
|
||||||
|
"session_hash": session_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
second_payload = {
|
||||||
|
"data": [],
|
||||||
|
"event_data": None,
|
||||||
|
"fn_index": 118,
|
||||||
|
"trigger_id": 159,
|
||||||
|
"session_hash": session_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
third_payload = {
|
||||||
|
"data": [None, temperature, top_p, max_tokens],
|
||||||
|
"event_data": None,
|
||||||
|
"fn_index": 119,
|
||||||
|
"trigger_id": 159,
|
||||||
|
"session_hash": session_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
return first_payload, second_payload, third_payload
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def create_async_generator(
|
||||||
|
cls, model: str, messages: Messages,
|
||||||
|
max_tokens: int = 2048,
|
||||||
|
temperature: float = 0.7,
|
||||||
|
top_p: float = 1,
|
||||||
|
proxy: str = None,
|
||||||
|
**kwargs
|
||||||
|
) -> AsyncResult:
|
||||||
|
if not model:
|
||||||
|
model = cls.default_model
|
||||||
|
if model in cls.model_aliases:
|
||||||
|
model = cls.model_aliases[model]
|
||||||
|
session_hash = cls._random_session_hash()
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
async with StreamSession(impersonate="chrome", headers=headers) as session:
|
||||||
|
first_payload, second_payload, third_payload = cls._build_payloads(model, session_hash, messages, max_tokens, temperature, top_p)
|
||||||
|
# Long stream GET
|
||||||
|
async def long_stream():
|
||||||
|
# POST 1
|
||||||
|
async with session.post(f"{cls.url}{cls.api_endpoint}", json=first_payload, proxy=proxy) as response:
|
||||||
|
await raise_for_status(response)
|
||||||
|
|
||||||
|
# POST 2
|
||||||
|
async with session.post(f"{cls.url}{cls.api_endpoint}", json=second_payload, proxy=proxy) as response:
|
||||||
|
await raise_for_status(response)
|
||||||
|
|
||||||
|
# POST 3
|
||||||
|
async with session.post(f"{cls.url}{cls.api_endpoint}", json=third_payload, proxy=proxy) as response:
|
||||||
|
await raise_for_status(response)
|
||||||
|
|
||||||
|
stream_url = f"{cls.url}/queue/data?session_hash={session_hash}"
|
||||||
|
async with session.get(stream_url, headers={"Accept": "text/event-stream"}, proxy=proxy) as response:
|
||||||
|
await raise_for_status(response)
|
||||||
|
text_position = 0
|
||||||
|
count = 0
|
||||||
|
async for line in response.iter_lines():
|
||||||
|
if line.startswith(b"data: "):
|
||||||
|
try:
|
||||||
|
msg = json.loads(line[6:])
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Failed to decode JSON from stream: {line}", e)
|
||||||
|
if msg.get("msg") == "process_generating":
|
||||||
|
data = msg["output"]["data"][1]
|
||||||
|
if data:
|
||||||
|
data = data[0]
|
||||||
|
if len(data) > 2:
|
||||||
|
if isinstance(data[2], list):
|
||||||
|
data[2] = data[2][-1]
|
||||||
|
content = data[2][text_position:]
|
||||||
|
if content.endswith("▌"):
|
||||||
|
content = content[:-2]
|
||||||
|
if content:
|
||||||
|
count += 1
|
||||||
|
yield count, content
|
||||||
|
text_position += len(content)
|
||||||
|
elif msg.get("msg") == "close_stream":
|
||||||
|
break
|
||||||
|
elif msg.get("msg") not in ("process_completed", "process_starts", "estimation"):
|
||||||
|
debug.log(f"Unexpected message: {msg}")
|
||||||
|
count = 0
|
||||||
|
async for count, chunk in long_stream():
|
||||||
|
yield chunk
|
||||||
|
if count == 0:
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
async for count, chunk in long_stream():
|
||||||
|
yield chunk
|
||||||
|
if count == 0:
|
||||||
|
raise RuntimeError("No response from server.")
|
||||||
|
if count == max_tokens:
|
||||||
|
yield FinishReason("length")
|
||||||
|
|
@ -10,7 +10,7 @@ from .BlackForestLabs_Flux1Dev import BlackForestLabs_Flux1Dev
|
||||||
from .BlackForestLabs_Flux1Schnell import BlackForestLabs_Flux1Schnell
|
from .BlackForestLabs_Flux1Schnell import BlackForestLabs_Flux1Schnell
|
||||||
from .CohereForAI_C4AI_Command import CohereForAI_C4AI_Command
|
from .CohereForAI_C4AI_Command import CohereForAI_C4AI_Command
|
||||||
from .DeepseekAI_JanusPro7b import DeepseekAI_JanusPro7b
|
from .DeepseekAI_JanusPro7b import DeepseekAI_JanusPro7b
|
||||||
from .G4F import G4F
|
from .LMArenaProvider import LMArenaProvider
|
||||||
from .Microsoft_Phi_4 import Microsoft_Phi_4
|
from .Microsoft_Phi_4 import Microsoft_Phi_4
|
||||||
from .Qwen_QVQ_72B import Qwen_QVQ_72B
|
from .Qwen_QVQ_72B import Qwen_QVQ_72B
|
||||||
from .Qwen_Qwen_2_5 import Qwen_Qwen_2_5
|
from .Qwen_Qwen_2_5 import Qwen_Qwen_2_5
|
||||||
|
|
@ -33,7 +33,7 @@ class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
BlackForestLabs_Flux1Schnell,
|
BlackForestLabs_Flux1Schnell,
|
||||||
CohereForAI_C4AI_Command,
|
CohereForAI_C4AI_Command,
|
||||||
DeepseekAI_JanusPro7b,
|
DeepseekAI_JanusPro7b,
|
||||||
G4F,
|
LMArenaProvider,
|
||||||
Microsoft_Phi_4,
|
Microsoft_Phi_4,
|
||||||
Qwen_QVQ_72B,
|
Qwen_QVQ_72B,
|
||||||
Qwen_Qwen_2_5,
|
Qwen_Qwen_2_5,
|
||||||
|
|
@ -88,7 +88,7 @@ class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
for provider in cls.providers:
|
for provider in cls.providers:
|
||||||
if model in provider.get_models():
|
if model in provider.get_models():
|
||||||
try:
|
try:
|
||||||
async for chunk in provider.create_async_generator(model, messages, images=images, **kwargs):
|
async for chunk in provider.create_async_generator(model, messages, media=media, **kwargs):
|
||||||
is_started = True
|
is_started = True
|
||||||
yield chunk
|
yield chunk
|
||||||
if is_started:
|
if is_started:
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,7 @@ from ...typing import AsyncResult, Messages
|
||||||
from ...errors import NoValidHarFileError
|
from ...errors import NoValidHarFileError
|
||||||
from ... import debug
|
from ... import debug
|
||||||
|
|
||||||
def cookies_to_dict():
|
class CopilotAccount(Copilot, AsyncAuthedProvider):
|
||||||
return Copilot._cookies if isinstance(Copilot._cookies, dict) else {c.name: c.value for c in Copilot._cookies}
|
|
||||||
|
|
||||||
class CopilotAccount(AsyncAuthedProvider, Copilot):
|
|
||||||
needs_auth = True
|
needs_auth = True
|
||||||
use_nodriver = True
|
use_nodriver = True
|
||||||
parent = "Copilot"
|
parent = "Copilot"
|
||||||
|
|
@ -23,17 +20,17 @@ class CopilotAccount(AsyncAuthedProvider, Copilot):
|
||||||
@classmethod
|
@classmethod
|
||||||
async def on_auth_async(cls, proxy: str = None, **kwargs) -> AsyncIterator:
|
async def on_auth_async(cls, proxy: str = None, **kwargs) -> AsyncIterator:
|
||||||
try:
|
try:
|
||||||
Copilot._access_token, Copilot._cookies = readHAR(cls.url)
|
cls._access_token, cls._cookies = readHAR(cls.url)
|
||||||
except NoValidHarFileError as h:
|
except NoValidHarFileError as h:
|
||||||
debug.log(f"Copilot: {h}")
|
debug.log(f"Copilot: {h}")
|
||||||
if has_nodriver:
|
if has_nodriver:
|
||||||
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
||||||
Copilot._access_token, Copilot._cookies = await get_access_token_and_cookies(cls.url, proxy)
|
cls._access_token, cls._cookies = await get_access_token_and_cookies(cls.url, proxy)
|
||||||
else:
|
else:
|
||||||
raise h
|
raise h
|
||||||
yield AuthResult(
|
yield AuthResult(
|
||||||
api_key=Copilot._access_token,
|
api_key=cls._access_token,
|
||||||
cookies=cookies_to_dict()
|
cookies=cls.cookies_to_dict()
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -44,9 +41,12 @@ class CopilotAccount(AsyncAuthedProvider, Copilot):
|
||||||
auth_result: AuthResult,
|
auth_result: AuthResult,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
Copilot._access_token = getattr(auth_result, "api_key")
|
cls._access_token = getattr(auth_result, "api_key")
|
||||||
Copilot._cookies = getattr(auth_result, "cookies")
|
cls._cookies = getattr(auth_result, "cookies")
|
||||||
Copilot.needs_auth = cls.needs_auth
|
async for chunk in cls.create_async_generator(model, messages, **kwargs):
|
||||||
for chunk in Copilot.create_completion(model, messages, **kwargs):
|
|
||||||
yield chunk
|
yield chunk
|
||||||
auth_result.cookies = cookies_to_dict()
|
auth_result.cookies = cls.cookies_to_dict()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cookies_to_dict(cls):
|
||||||
|
return cls._cookies if isinstance(cls._cookies, dict) else {c.name: c.value for c in cls._cookies}
|
||||||
|
|
@ -146,7 +146,7 @@ async def get_access_token_and_user_agent(url: str, proxy: str = None):
|
||||||
browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
|
browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
|
||||||
try:
|
try:
|
||||||
page = await browser.get(url)
|
page = await browser.get(url)
|
||||||
user_agent = await page.evaluate("navigator.userAgent")
|
user_agent = await page.evaluate("navigator.userAgent", return_by_value=True)
|
||||||
access_token = None
|
access_token = None
|
||||||
while access_token is None:
|
while access_token is None:
|
||||||
access_token = await page.evaluate("""
|
access_token = await page.evaluate("""
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
messages: Messages,
|
messages: Messages,
|
||||||
auth_result: AuthResult,
|
auth_result: AuthResult,
|
||||||
proxy: str = None,
|
proxy: str = None,
|
||||||
timeout: int = 180,
|
timeout: int = 360,
|
||||||
auto_continue: bool = False,
|
auto_continue: bool = False,
|
||||||
action: str = "next",
|
action: str = "next",
|
||||||
conversation: Conversation = None,
|
conversation: Conversation = None,
|
||||||
|
|
@ -447,7 +447,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
link = sources.list[int(match.group(1))]["url"]
|
link = sources.list[int(match.group(1))]["url"]
|
||||||
return f"[[{int(match.group(1))+1}]]({link})"
|
return f"[[{int(match.group(1))+1}]]({link})"
|
||||||
return f" [{int(match.group(1))+1}]"
|
return f" [{int(match.group(1))+1}]"
|
||||||
buffer = re.sub(r'(?:cite\nturn0search|cite\nturn0news|turn0news)(\d+)', replacer, buffer)
|
buffer = re.sub(r'(?:cite\nturn[0-9]+|turn[0-9]+)(?:search|news|view)(\d+)', replacer, buffer)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
yield buffer
|
yield buffer
|
||||||
|
|
@ -489,25 +489,39 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
if "type" in line:
|
if "type" in line:
|
||||||
if line["type"] == "title_generation":
|
if line["type"] == "title_generation":
|
||||||
yield TitleGeneration(line["title"])
|
yield TitleGeneration(line["title"])
|
||||||
|
fields.p = line.get("p", fields.p)
|
||||||
|
if fields.p.startswith("/message/content/thoughts"):
|
||||||
|
if fields.p.endswith("/content"):
|
||||||
|
if fields.thoughts_summary:
|
||||||
|
yield Reasoning(token="", status=fields.thoughts_summary)
|
||||||
|
fields.thoughts_summary = ""
|
||||||
|
yield Reasoning(token=line.get("v"))
|
||||||
|
elif fields.p.endswith("/summary"):
|
||||||
|
fields.thoughts_summary += line.get("v")
|
||||||
|
return
|
||||||
if "v" in line:
|
if "v" in line:
|
||||||
v = line.get("v")
|
v = line.get("v")
|
||||||
if isinstance(v, str) and fields.is_recipient:
|
if isinstance(v, str) and fields.recipient == "all":
|
||||||
if "p" not in line or line.get("p") == "/message/content/parts/0":
|
if "p" not in line or line.get("p") == "/message/content/parts/0":
|
||||||
yield Reasoning(token=v) if fields.is_thinking else v
|
yield Reasoning(token=v) if fields.is_thinking else v
|
||||||
elif isinstance(v, list):
|
elif isinstance(v, list):
|
||||||
for m in v:
|
for m in v:
|
||||||
if m.get("p") == "/message/content/parts/0" and fields.is_recipient:
|
if m.get("p") == "/message/content/parts/0" and fields.recipient == "all":
|
||||||
yield m.get("v")
|
yield m.get("v")
|
||||||
elif m.get("p") == "/message/metadata/search_result_groups":
|
elif m.get("p") == "/message/metadata/search_result_groups":
|
||||||
for entry in [p.get("entries") for p in m.get("v")]:
|
for entry in [p.get("entries") for p in m.get("v")]:
|
||||||
for link in entry:
|
for link in entry:
|
||||||
sources.add_source(link)
|
sources.add_source(link)
|
||||||
elif re.match(r"^/message/metadata/content_references/\d+$", m.get("p")):
|
elif m.get("p") == "/message/metadata/content_references":
|
||||||
|
for entry in m.get("v"):
|
||||||
|
for link in entry.get("sources", []):
|
||||||
|
sources.add_source(link)
|
||||||
|
elif m.get("p") and re.match(r"^/message/metadata/content_references/\d+$", m.get("p")):
|
||||||
sources.add_source(m.get("v"))
|
sources.add_source(m.get("v"))
|
||||||
elif m.get("p") == "/message/metadata/finished_text":
|
elif m.get("p") == "/message/metadata/finished_text":
|
||||||
fields.is_thinking = False
|
fields.is_thinking = False
|
||||||
yield Reasoning(status=m.get("v"))
|
yield Reasoning(status=m.get("v"))
|
||||||
elif m.get("p") == "/message/metadata":
|
elif m.get("p") == "/message/metadata" and fields.recipient == "all":
|
||||||
fields.finish_reason = m.get("v", {}).get("finish_details", {}).get("type")
|
fields.finish_reason = m.get("v", {}).get("finish_details", {}).get("type")
|
||||||
break
|
break
|
||||||
elif isinstance(v, dict):
|
elif isinstance(v, dict):
|
||||||
|
|
@ -515,8 +529,8 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
fields.conversation_id = v.get("conversation_id")
|
fields.conversation_id = v.get("conversation_id")
|
||||||
debug.log(f"OpenaiChat: New conversation: {fields.conversation_id}")
|
debug.log(f"OpenaiChat: New conversation: {fields.conversation_id}")
|
||||||
m = v.get("message", {})
|
m = v.get("message", {})
|
||||||
fields.is_recipient = m.get("recipient", "all") == "all"
|
fields.recipient = m.get("recipient", fields.recipient)
|
||||||
if fields.is_recipient:
|
if fields.recipient == "all":
|
||||||
c = m.get("content", {})
|
c = m.get("content", {})
|
||||||
if c.get("content_type") == "text" and m.get("author", {}).get("role") == "tool" and "initial_text" in m.get("metadata", {}):
|
if c.get("content_type") == "text" and m.get("author", {}).get("role") == "tool" and "initial_text" in m.get("metadata", {}):
|
||||||
fields.is_thinking = True
|
fields.is_thinking = True
|
||||||
|
|
@ -578,14 +592,17 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
cls._set_api_key(api_key)
|
cls._set_api_key(api_key)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
await get_request_config(cls.request_config, proxy)
|
cls.request_config = await get_request_config(cls.request_config, proxy)
|
||||||
|
if cls.request_config is None:
|
||||||
|
cls.request_config = RequestConfig()
|
||||||
cls._create_request_args(cls.request_config.cookies, cls.request_config.headers)
|
cls._create_request_args(cls.request_config.cookies, cls.request_config.headers)
|
||||||
if cls.request_config.access_token is not None or cls.needs_auth:
|
if cls.needs_auth and cls.request_config.access_token is None:
|
||||||
|
raise NoValidHarFileError(f"Missing access token")
|
||||||
if not cls._set_api_key(cls.request_config.access_token):
|
if not cls._set_api_key(cls.request_config.access_token):
|
||||||
raise NoValidHarFileError(f"Access token is not valid: {cls.request_config.access_token}")
|
raise NoValidHarFileError(f"Access token is not valid: {cls.request_config.access_token}")
|
||||||
except NoValidHarFileError:
|
except NoValidHarFileError:
|
||||||
if has_nodriver:
|
if has_nodriver:
|
||||||
if cls._api_key is None:
|
if cls.request_config.access_token is None:
|
||||||
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
|
||||||
await cls.nodriver_auth(proxy)
|
await cls.nodriver_auth(proxy)
|
||||||
else:
|
else:
|
||||||
|
|
@ -622,15 +639,18 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
await page.send(nodriver.cdp.network.enable())
|
await page.send(nodriver.cdp.network.enable())
|
||||||
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
||||||
page = await browser.get(cls.url)
|
page = await browser.get(cls.url)
|
||||||
user_agent = await page.evaluate("window.navigator.userAgent")
|
user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True)
|
||||||
await page.select("#prompt-textarea", 240)
|
while not await page.evaluate("document.getElementById('prompt-textarea').id"):
|
||||||
await page.evaluate("document.getElementById('prompt-textarea').innerText = 'Hello'")
|
await asyncio.sleep(1)
|
||||||
await page.select("[data-testid=\"send-button\"]", 30)
|
while not await page.evaluate("document.querySelector('[data-testid=\"send-button\"]').type"):
|
||||||
|
await asyncio.sleep(1)
|
||||||
await page.evaluate("document.querySelector('[data-testid=\"send-button\"]').click()")
|
await page.evaluate("document.querySelector('[data-testid=\"send-button\"]').click()")
|
||||||
while True:
|
while True:
|
||||||
body = await page.evaluate("JSON.stringify(window.__remixContext)")
|
body = await page.evaluate("JSON.stringify(window.__remixContext)", return_by_value=True)
|
||||||
|
if hasattr(body, "value"):
|
||||||
|
body = body.value
|
||||||
if body:
|
if body:
|
||||||
match = re.search(r'"accessToken":"(.*?)"', body)
|
match = re.search(r'"accessToken":"(.+?)"', body)
|
||||||
if match:
|
if match:
|
||||||
cls._api_key = match.group(1)
|
cls._api_key = match.group(1)
|
||||||
break
|
break
|
||||||
|
|
@ -674,6 +694,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _set_api_key(cls, api_key: str):
|
def _set_api_key(cls, api_key: str):
|
||||||
|
cls._api_key = api_key
|
||||||
if api_key:
|
if api_key:
|
||||||
exp = api_key.split(".")[1]
|
exp = api_key.split(".")[1]
|
||||||
exp = (exp + "=" * (4 - len(exp) % 4)).encode()
|
exp = (exp + "=" * (4 - len(exp) % 4)).encode()
|
||||||
|
|
@ -681,11 +702,11 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
|
||||||
debug.log(f"OpenaiChat: API key expires at\n {cls._expires} we have:\n {time.time()}")
|
debug.log(f"OpenaiChat: API key expires at\n {cls._expires} we have:\n {time.time()}")
|
||||||
if time.time() > cls._expires:
|
if time.time() > cls._expires:
|
||||||
debug.log(f"OpenaiChat: API key is expired")
|
debug.log(f"OpenaiChat: API key is expired")
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
cls._api_key = api_key
|
|
||||||
cls._headers["authorization"] = f"Bearer {api_key}"
|
cls._headers["authorization"] = f"Bearer {api_key}"
|
||||||
return True
|
return True
|
||||||
return False
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _update_cookie_header(cls):
|
def _update_cookie_header(cls):
|
||||||
|
|
@ -700,10 +721,12 @@ class Conversation(JsonConversation):
|
||||||
self.conversation_id = conversation_id
|
self.conversation_id = conversation_id
|
||||||
self.message_id = message_id
|
self.message_id = message_id
|
||||||
self.finish_reason = finish_reason
|
self.finish_reason = finish_reason
|
||||||
self.is_recipient = False
|
self.recipient = "all"
|
||||||
self.parent_message_id = message_id if parent_message_id is None else parent_message_id
|
self.parent_message_id = message_id if parent_message_id is None else parent_message_id
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
self.is_thinking = is_thinking
|
self.is_thinking = is_thinking
|
||||||
|
self.p = None
|
||||||
|
self.thoughts_summary = ""
|
||||||
|
|
||||||
def get_cookies(
|
def get_cookies(
|
||||||
urls: Optional[Iterator[str]] = None
|
urls: Optional[Iterator[str]] = None
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ def get_har_files():
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.endswith(".har"):
|
if file.endswith(".har"):
|
||||||
harPath.append(os.path.join(root, file))
|
harPath.append(os.path.join(root, file))
|
||||||
|
break
|
||||||
if not harPath:
|
if not harPath:
|
||||||
raise NoValidHarFileError("No .har file found")
|
raise NoValidHarFileError("No .har file found")
|
||||||
harPath.sort(key=lambda x: os.path.getmtime(x))
|
harPath.sort(key=lambda x: os.path.getmtime(x))
|
||||||
|
|
@ -86,8 +87,6 @@ def readHAR(request_config: RequestConfig):
|
||||||
request_config.cookies = {c['name']: c['value'] for c in v['request']['cookies']}
|
request_config.cookies = {c['name']: c['value'] for c in v['request']['cookies']}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug.log(f"Error on read headers: {e}")
|
debug.log(f"Error on read headers: {e}")
|
||||||
if request_config.proof_token is None:
|
|
||||||
raise NoValidHarFileError("No proof_token found in .har files")
|
|
||||||
|
|
||||||
def get_headers(entry) -> dict:
|
def get_headers(entry) -> dict:
|
||||||
return {h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')}
|
return {h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')}
|
||||||
|
|
@ -152,8 +151,9 @@ def getN() -> str:
|
||||||
return base64.b64encode(timestamp.encode()).decode()
|
return base64.b64encode(timestamp.encode()).decode()
|
||||||
|
|
||||||
async def get_request_config(request_config: RequestConfig, proxy: str) -> RequestConfig:
|
async def get_request_config(request_config: RequestConfig, proxy: str) -> RequestConfig:
|
||||||
if request_config.proof_token is None:
|
|
||||||
readHAR(request_config)
|
readHAR(request_config)
|
||||||
if request_config.arkose_request is not None:
|
if request_config.arkose_request is not None:
|
||||||
request_config.arkose_token = await sendRequest(genArkReq(request_config.arkose_request), proxy)
|
request_config.arkose_token = await sendRequest(genArkReq(request_config.arkose_request), proxy)
|
||||||
|
if request_config.proof_token is None:
|
||||||
|
raise NoValidHarFileError("No proof_token found in .har files")
|
||||||
return request_config
|
return request_config
|
||||||
|
|
|
||||||
|
|
@ -309,9 +309,9 @@ class Api:
|
||||||
if credentials is not None and credentials.credentials != "secret":
|
if credentials is not None and credentials.credentials != "secret":
|
||||||
config.api_key = credentials.credentials
|
config.api_key = credentials.credentials
|
||||||
|
|
||||||
conversation = None
|
conversation = config.conversation
|
||||||
return_conversation = config.return_conversation
|
return_conversation = config.return_conversation
|
||||||
if conversation is not None:
|
if conversation:
|
||||||
conversation = JsonConversation(**conversation)
|
conversation = JsonConversation(**conversation)
|
||||||
return_conversation = True
|
return_conversation = True
|
||||||
elif config.conversation_id is not None and config.provider is not None:
|
elif config.conversation_id is not None and config.provider is not None:
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ async def async_iter_response(
|
||||||
|
|
||||||
if stream:
|
if stream:
|
||||||
chat_completion = ChatCompletionChunk.model_construct(
|
chat_completion = ChatCompletionChunk.model_construct(
|
||||||
None, finish_reason, completion_id, int(time.time()), usage=usage
|
None, finish_reason, completion_id, int(time.time()), usage=usage, conversation=conversation
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if response_format is not None and "type" in response_format:
|
if response_format is not None and "type" in response_format:
|
||||||
|
|
@ -228,7 +228,7 @@ async def async_iter_response(
|
||||||
**filter_none(
|
**filter_none(
|
||||||
tool_calls=[ToolCallModel.model_construct(**tool_call) for tool_call in tool_calls]
|
tool_calls=[ToolCallModel.model_construct(**tool_call) for tool_call in tool_calls]
|
||||||
) if tool_calls is not None else {},
|
) if tool_calls is not None else {},
|
||||||
conversation=None if conversation is None else conversation.get_dict()
|
conversation=conversation
|
||||||
)
|
)
|
||||||
if provider is not None:
|
if provider is not None:
|
||||||
chat_completion.provider = provider.name
|
chat_completion.provider = provider.name
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from ..client.helper import filter_markdown
|
||||||
from .helper import filter_none
|
from .helper import filter_none
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, field_serializer
|
||||||
except ImportError:
|
except ImportError:
|
||||||
class BaseModel():
|
class BaseModel():
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -19,6 +19,11 @@ except ImportError:
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
setattr(new, key, value)
|
setattr(new, key, value)
|
||||||
return new
|
return new
|
||||||
|
class field_serializer():
|
||||||
|
def __init__(self, field_name):
|
||||||
|
self.field_name = field_name
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return args[0]
|
||||||
|
|
||||||
class BaseModel(BaseModel):
|
class BaseModel(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -72,6 +77,7 @@ class ChatCompletionChunk(BaseModel):
|
||||||
provider: Optional[str]
|
provider: Optional[str]
|
||||||
choices: List[ChatCompletionDeltaChoice]
|
choices: List[ChatCompletionDeltaChoice]
|
||||||
usage: UsageModel
|
usage: UsageModel
|
||||||
|
conversation: dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def model_construct(
|
def model_construct(
|
||||||
|
|
@ -80,7 +86,8 @@ class ChatCompletionChunk(BaseModel):
|
||||||
finish_reason: str,
|
finish_reason: str,
|
||||||
completion_id: str = None,
|
completion_id: str = None,
|
||||||
created: int = None,
|
created: int = None,
|
||||||
usage: UsageModel = None
|
usage: UsageModel = None,
|
||||||
|
conversation: dict = None
|
||||||
):
|
):
|
||||||
return super().model_construct(
|
return super().model_construct(
|
||||||
id=f"chatcmpl-{completion_id}" if completion_id else None,
|
id=f"chatcmpl-{completion_id}" if completion_id else None,
|
||||||
|
|
@ -92,9 +99,15 @@ class ChatCompletionChunk(BaseModel):
|
||||||
ChatCompletionDelta.model_construct(content),
|
ChatCompletionDelta.model_construct(content),
|
||||||
finish_reason
|
finish_reason
|
||||||
)],
|
)],
|
||||||
**filter_none(usage=usage)
|
**filter_none(usage=usage, conversation=conversation)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@field_serializer('conversation')
|
||||||
|
def serialize_conversation(self, conversation: dict):
|
||||||
|
if hasattr(conversation, "get_dict"):
|
||||||
|
return conversation.get_dict()
|
||||||
|
return conversation
|
||||||
|
|
||||||
class ChatCompletionMessage(BaseModel):
|
class ChatCompletionMessage(BaseModel):
|
||||||
role: str
|
role: str
|
||||||
content: str
|
content: str
|
||||||
|
|
@ -104,6 +117,10 @@ class ChatCompletionMessage(BaseModel):
|
||||||
def model_construct(cls, content: str, tool_calls: list = None):
|
def model_construct(cls, content: str, tool_calls: list = None):
|
||||||
return super().model_construct(role="assistant", content=content, **filter_none(tool_calls=tool_calls))
|
return super().model_construct(role="assistant", content=content, **filter_none(tool_calls=tool_calls))
|
||||||
|
|
||||||
|
@field_serializer('content')
|
||||||
|
def serialize_content(self, content: str):
|
||||||
|
return str(content)
|
||||||
|
|
||||||
def save(self, filepath: str, allowd_types = None):
|
def save(self, filepath: str, allowd_types = None):
|
||||||
if hasattr(self.content, "data"):
|
if hasattr(self.content, "data"):
|
||||||
os.rename(self.content.data.replace("/media", images_dir), filepath)
|
os.rename(self.content.data.replace("/media", images_dir), filepath)
|
||||||
|
|
@ -160,6 +177,12 @@ class ChatCompletion(BaseModel):
|
||||||
**filter_none(usage=usage, conversation=conversation)
|
**filter_none(usage=usage, conversation=conversation)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@field_serializer('conversation')
|
||||||
|
def serialize_conversation(self, conversation: dict):
|
||||||
|
if hasattr(conversation, "get_dict"):
|
||||||
|
return conversation.get_dict()
|
||||||
|
return conversation
|
||||||
|
|
||||||
class ChatCompletionDelta(BaseModel):
|
class ChatCompletionDelta(BaseModel):
|
||||||
role: str
|
role: str
|
||||||
content: str
|
content: str
|
||||||
|
|
@ -168,6 +191,10 @@ class ChatCompletionDelta(BaseModel):
|
||||||
def model_construct(cls, content: Optional[str]):
|
def model_construct(cls, content: Optional[str]):
|
||||||
return super().model_construct(role="assistant", content=content)
|
return super().model_construct(role="assistant", content=content)
|
||||||
|
|
||||||
|
@field_serializer('content')
|
||||||
|
def serialize_content(self, content: str):
|
||||||
|
return str(content)
|
||||||
|
|
||||||
class ChatCompletionDeltaChoice(BaseModel):
|
class ChatCompletionDeltaChoice(BaseModel):
|
||||||
index: int
|
index: int
|
||||||
delta: ChatCompletionDelta
|
delta: ChatCompletionDelta
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,11 @@ DOMAINS = [
|
||||||
".google.com",
|
".google.com",
|
||||||
"www.whiterabbitneo.com",
|
"www.whiterabbitneo.com",
|
||||||
"huggingface.co",
|
"huggingface.co",
|
||||||
|
".huggingface.co"
|
||||||
"chat.reka.ai",
|
"chat.reka.ai",
|
||||||
"chatgpt.com",
|
"chatgpt.com",
|
||||||
".cerebras.ai",
|
".cerebras.ai",
|
||||||
"github.com",
|
"github.com",
|
||||||
"huggingface.co",
|
|
||||||
".huggingface.co"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if has_browser_cookie3 and os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
|
if has_browser_cookie3 and os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
|
||||||
|
|
@ -152,6 +151,7 @@ def read_cookie_files(dirPath: str = None):
|
||||||
harFiles.append(os.path.join(root, file))
|
harFiles.append(os.path.join(root, file))
|
||||||
elif file.endswith(".json"):
|
elif file.endswith(".json"):
|
||||||
cookieFiles.append(os.path.join(root, file))
|
cookieFiles.append(os.path.join(root, file))
|
||||||
|
break
|
||||||
|
|
||||||
CookiesConfig.cookies = {}
|
CookiesConfig.cookies = {}
|
||||||
for path in harFiles:
|
for path in harFiles:
|
||||||
|
|
|
||||||
|
|
@ -169,15 +169,15 @@
|
||||||
if (errorVideo < 3 || !refreshOnHide) {
|
if (errorVideo < 3 || !refreshOnHide) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (skipRefresh > 0) {
|
||||||
|
skipRefresh -= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (errorImage < 3) {
|
if (errorImage < 3) {
|
||||||
imageFeed.src = "/search/image+g4f?skip=" + skipImage;
|
imageFeed.src = "/search/image+g4f?skip=" + skipImage;
|
||||||
skipImage++;
|
skipImage++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (skipRefresh > 0) {
|
|
||||||
skipRefresh -= 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
imageFeed.classList.remove("hidden");
|
imageFeed.classList.remove("hidden");
|
||||||
imageFeed.src = images.shift();
|
imageFeed.src = images.shift();
|
||||||
|
|
@ -194,10 +194,13 @@
|
||||||
imageFeed.onload = () => {
|
imageFeed.onload = () => {
|
||||||
imageFeed.classList.remove("hidden");
|
imageFeed.classList.remove("hidden");
|
||||||
gradient.classList.add("hidden");
|
gradient.classList.add("hidden");
|
||||||
|
errorImage = 0;
|
||||||
};
|
};
|
||||||
imageFeed.onclick = () => {
|
imageFeed.onclick = () => {
|
||||||
imageFeed.src = "/search/image?random=" + Math.random();
|
imageFeed.src = "/search/image?random=" + Math.random();
|
||||||
skipRefresh = 2;
|
if (skipRefresh < 4) {
|
||||||
|
skipRefresh += 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1533,6 +1533,7 @@ form textarea {
|
||||||
.chat-top-panel .convo-title {
|
.chat-top-panel .convo-title {
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1581,7 +1582,6 @@ form textarea {
|
||||||
}
|
}
|
||||||
.chat-body {
|
.chat-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -1613,11 +1613,25 @@ form textarea {
|
||||||
border: 1px dashed var(--conversations);
|
border: 1px dashed var(--conversations);
|
||||||
box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.75);
|
box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.75);
|
||||||
}
|
}
|
||||||
.white .chat-footer .send-buttons button {
|
.suggestions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.suggestions .suggestion {
|
||||||
|
background: var(--blur-bg);
|
||||||
|
color: var(--colour-3);
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0 2px 0 4px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px dashed var(--conversations);
|
||||||
|
}
|
||||||
|
.white .chat-footer .send-buttons button, .white .suggestions .suggestion {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: var(--blur-border);
|
border-color: var(--blur-border);
|
||||||
}
|
}
|
||||||
.chat-footer .send-buttons button:hover {
|
.chat-footer .send-buttons button:hover, .suggestions .suggestion:hover {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background-color: var(--button-hover);
|
background-color: var(--button-hover);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ let wakeLock = null;
|
||||||
let countTokensEnabled = true;
|
let countTokensEnabled = true;
|
||||||
let reloadConversation = true;
|
let reloadConversation = true;
|
||||||
let privateConversation = null;
|
let privateConversation = null;
|
||||||
|
let suggestions = null;
|
||||||
|
|
||||||
userInput.addEventListener("blur", () => {
|
userInput.addEventListener("blur", () => {
|
||||||
document.documentElement.scrollTop = 0;
|
document.documentElement.scrollTop = 0;
|
||||||
|
|
@ -119,9 +120,9 @@ function filter_message(text) {
|
||||||
if (Array.isArray(text)) {
|
if (Array.isArray(text)) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
return text.replaceAll(
|
return filter_message_content(text.replaceAll(
|
||||||
/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, ""
|
/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, ""
|
||||||
).replace(/ \[aborted\]$/g, "").replace(/ \[error\]$/g, "");
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
function filter_message_content(text) {
|
function filter_message_content(text) {
|
||||||
|
|
@ -468,7 +469,7 @@ const register_message_buttons = async () => {
|
||||||
el.dataset.click = true;
|
el.dataset.click = true;
|
||||||
const message_el = get_message_el(el);
|
const message_el = get_message_el(el);
|
||||||
el.addEventListener("click", async () => {
|
el.addEventListener("click", async () => {
|
||||||
iframe.src = `/qrcode/${window.conversation_id}#${message_el.dataset.index}`;
|
iframe.src = window.conversation_id ? `/qrcode/${window.conversation_id}#${message_el.dataset.index}` : '/qrcode';
|
||||||
iframe_container.classList.remove("hidden");
|
iframe_container.classList.remove("hidden");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -933,9 +934,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
|
||||||
} else if (message.type == "login") {
|
} else if (message.type == "login") {
|
||||||
update_message(content_map, message_id, markdown_render(message.login), scroll);
|
update_message(content_map, message_id, markdown_render(message.login), scroll);
|
||||||
} else if (message.type == "finish") {
|
} else if (message.type == "finish") {
|
||||||
if (!finish_storage[message_id]) {
|
|
||||||
finish_storage[message_id] = message.finish;
|
finish_storage[message_id] = message.finish;
|
||||||
}
|
|
||||||
} else if (message.type == "usage") {
|
} else if (message.type == "usage") {
|
||||||
usage_storage[message_id] = message.usage;
|
usage_storage[message_id] = message.usage;
|
||||||
} else if (message.type == "reasoning") {
|
} else if (message.type == "reasoning") {
|
||||||
|
|
@ -950,7 +949,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
|
||||||
} if (message.token) {
|
} if (message.token) {
|
||||||
reasoning_storage[message_id].text += message.token;
|
reasoning_storage[message_id].text += message.token;
|
||||||
}
|
}
|
||||||
update_message(content_map, message_id, render_reasoning(reasoning_storage[message_id]), scroll);
|
update_message(content_map, message_id, null, scroll);
|
||||||
} else if (message.type == "parameters") {
|
} else if (message.type == "parameters") {
|
||||||
if (!parameters_storage[provider]) {
|
if (!parameters_storage[provider]) {
|
||||||
parameters_storage[provider] = {};
|
parameters_storage[provider] = {};
|
||||||
|
|
@ -958,6 +957,8 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
|
||||||
Object.entries(message.parameters).forEach(([key, value]) => {
|
Object.entries(message.parameters).forEach(([key, value]) => {
|
||||||
parameters_storage[provider][key] = value;
|
parameters_storage[provider][key] = value;
|
||||||
});
|
});
|
||||||
|
} else if (message.type == "suggestions") {
|
||||||
|
suggestions = message.suggestions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -998,6 +999,9 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
|
||||||
if (scroll) {
|
if (scroll) {
|
||||||
await lazy_scroll_to_bottom();
|
await lazy_scroll_to_bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let suggestions_el = chatBody.querySelector('.suggestions');
|
||||||
|
suggestions_el ? suggestions_el.remove() : null;
|
||||||
if (countTokensEnabled) {
|
if (countTokensEnabled) {
|
||||||
let count_total = chatBody.querySelector('.count_total');
|
let count_total = chatBody.querySelector('.count_total');
|
||||||
count_total ? count_total.parentElement.removeChild(count_total) : null;
|
count_total ? count_total.parentElement.removeChild(count_total) : null;
|
||||||
|
|
@ -1507,7 +1511,7 @@ const load_conversation = async (conversation, scroll=true) => {
|
||||||
} else if (reason == "stop" && buffer.split("```").length - 1 % 2 === 1) {
|
} else if (reason == "stop" && buffer.split("```").length - 1 % 2 === 1) {
|
||||||
reason = "length";
|
reason = "length";
|
||||||
}
|
}
|
||||||
if (reason == "length" || reason == "max_tokens" || reason == "error") {
|
if (reason != "stop") {
|
||||||
actions.push("continue")
|
actions.push("continue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1578,8 +1582,23 @@ const load_conversation = async (conversation, scroll=true) => {
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
chatBody.innerHTML = elements.join("");
|
||||||
|
|
||||||
if (countTokensEnabled && window.GPTTokenizer_cl100k_base) {
|
if (suggestions) {
|
||||||
|
const suggestions_el = document.createElement("div");
|
||||||
|
suggestions_el.classList.add("suggestions");
|
||||||
|
suggestions.forEach((suggestion)=> {
|
||||||
|
const el = document.createElement("button");
|
||||||
|
el.classList.add("suggestion");
|
||||||
|
el.innerHTML = `<span>${escapeHtml(suggestion)}</span> <i class="fa-solid fa-turn-up"></i>`;
|
||||||
|
el.onclick = async () => {
|
||||||
|
await handle_ask(true, suggestion);
|
||||||
|
}
|
||||||
|
suggestions_el.appendChild(el);
|
||||||
|
});
|
||||||
|
chatBody.appendChild(suggestions_el);
|
||||||
|
suggestions = null;
|
||||||
|
} else if (countTokensEnabled && window.GPTTokenizer_cl100k_base) {
|
||||||
const has_media = messages.filter((item)=>Array.isArray(item.content)).length > 0;
|
const has_media = messages.filter((item)=>Array.isArray(item.content)).length > 0;
|
||||||
if (!has_media) {
|
if (!has_media) {
|
||||||
const filtered = prepare_messages(messages, null, true, false);
|
const filtered = prepare_messages(messages, null, true, false);
|
||||||
|
|
@ -1587,13 +1606,15 @@ const load_conversation = async (conversation, scroll=true) => {
|
||||||
last_model = last_model?.startsWith("gpt-3") ? "gpt-3.5-turbo" : "gpt-4"
|
last_model = last_model?.startsWith("gpt-3") ? "gpt-3.5-turbo" : "gpt-4"
|
||||||
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
|
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
|
||||||
if (count_total > 0) {
|
if (count_total > 0) {
|
||||||
elements.push(`<div class="count_total">(${count_total} total tokens)</div>`);
|
const count_total_el = document.createElement("div");
|
||||||
|
count_total_el.classList.add("count_total");
|
||||||
|
count_total_el.innerText = `(${count_total} total tokens)`;
|
||||||
|
chatBody.appendChild(count_total_el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chatBody.innerHTML = elements.join("");
|
|
||||||
await register_message_buttons();
|
await register_message_buttons();
|
||||||
highlight(chatBody);
|
highlight(chatBody);
|
||||||
regenerate_button.classList.remove("regenerate-hidden");
|
regenerate_button.classList.remove("regenerate-hidden");
|
||||||
|
|
@ -2079,8 +2100,10 @@ function count_words_and_tokens(text, model, completion_tokens, prompt_tokens) {
|
||||||
function update_message(content_map, message_id, content = null, scroll = true) {
|
function update_message(content_map, message_id, content = null, scroll = true) {
|
||||||
content_map.update_timeouts.push(setTimeout(() => {
|
content_map.update_timeouts.push(setTimeout(() => {
|
||||||
if (!content) {
|
if (!content) {
|
||||||
if (reasoning_storage[message_id]) {
|
if (reasoning_storage[message_id] && message_storage[message_id]) {
|
||||||
content = render_reasoning(reasoning_storage[message_id], true) + markdown_render(message_storage[message_id]);
|
content = render_reasoning(reasoning_storage[message_id], true) + markdown_render(message_storage[message_id]);
|
||||||
|
} else if (reasoning_storage[message_id]) {
|
||||||
|
content = render_reasoning(reasoning_storage[message_id]);
|
||||||
} else {
|
} else {
|
||||||
content = markdown_render(message_storage[message_id]);
|
content = markdown_render(message_storage[message_id]);
|
||||||
}
|
}
|
||||||
|
|
@ -2097,10 +2120,9 @@ function update_message(content_map, message_id, content = null, scroll = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error_storage[message_id]) {
|
if (error_storage[message_id]) {
|
||||||
content_map.inner.innerHTML = message + markdown_render(`**An error occured:** ${error_storage[message_id]}`);
|
content += markdown_render(`**An error occured:** ${error_storage[message_id]}`);
|
||||||
} else {
|
|
||||||
content_map.inner.innerHTML = content;
|
|
||||||
}
|
}
|
||||||
|
content_map.inner.innerHTML = content;
|
||||||
if (countTokensEnabled) {
|
if (countTokensEnabled) {
|
||||||
content_map.count.innerText = count_words_and_tokens(
|
content_map.count.innerText = count_words_and_tokens(
|
||||||
(reasoning_storage[message_id] ? reasoning_storage[message_id].text : "")
|
(reasoning_storage[message_id] ? reasoning_storage[message_id].text : "")
|
||||||
|
|
@ -2484,8 +2506,14 @@ async function on_api() {
|
||||||
|
|
||||||
const hide_systemPrompt = document.getElementById("hide-systemPrompt")
|
const hide_systemPrompt = document.getElementById("hide-systemPrompt")
|
||||||
const slide_systemPrompt_icon = document.querySelector(".slide-header i");
|
const slide_systemPrompt_icon = document.querySelector(".slide-header i");
|
||||||
|
document.querySelector(".slide-header")?.addEventListener("click", () => {
|
||||||
|
const checked = slide_systemPrompt_icon.classList.contains("fa-angles-up");
|
||||||
|
chatPrompt.classList[checked ? "add": "remove"]("hidden");
|
||||||
|
slide_systemPrompt_icon.classList[checked ? "remove": "add"]("fa-angles-up");
|
||||||
|
slide_systemPrompt_icon.classList[checked ? "add": "remove"]("fa-angles-down");
|
||||||
|
});
|
||||||
if (hide_systemPrompt.checked) {
|
if (hide_systemPrompt.checked) {
|
||||||
chatPrompt.classList.add("hidden");
|
slide_systemPrompt_icon.click();
|
||||||
}
|
}
|
||||||
hide_systemPrompt.addEventListener('change', async (event) => {
|
hide_systemPrompt.addEventListener('change', async (event) => {
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
|
|
@ -2494,12 +2522,6 @@ async function on_api() {
|
||||||
chatPrompt.classList.remove("hidden");
|
chatPrompt.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.querySelector(".slide-header")?.addEventListener("click", () => {
|
|
||||||
const checked = slide_systemPrompt_icon.classList.contains("fa-angles-up");
|
|
||||||
chatPrompt.classList[checked ? "add": "remove"]("hidden");
|
|
||||||
slide_systemPrompt_icon.classList[checked ? "remove": "add"]("fa-angles-up");
|
|
||||||
slide_systemPrompt_icon.classList[checked ? "add": "remove"]("fa-angles-down");
|
|
||||||
});
|
|
||||||
const userInputHeight = document.getElementById("message-input-height");
|
const userInputHeight = document.getElementById("message-input-height");
|
||||||
if (userInputHeight) {
|
if (userInputHeight) {
|
||||||
if (userInputHeight.value) {
|
if (userInputHeight.value) {
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,8 @@ class Api:
|
||||||
yield self._format_json("content", chunk.to_string())
|
yield self._format_json("content", chunk.to_string())
|
||||||
elif isinstance(chunk, AudioResponse):
|
elif isinstance(chunk, AudioResponse):
|
||||||
yield self._format_json("content", str(chunk))
|
yield self._format_json("content", str(chunk))
|
||||||
|
elif isinstance(chunk, SuggestedFollowups):
|
||||||
|
yield self._format_json("suggestions", chunk.suggestions)
|
||||||
elif isinstance(chunk, DebugResponse):
|
elif isinstance(chunk, DebugResponse):
|
||||||
yield self._format_json("log", chunk.log)
|
yield self._format_json("log", chunk.log)
|
||||||
elif isinstance(chunk, RawResponse):
|
elif isinstance(chunk, RawResponse):
|
||||||
|
|
|
||||||
|
|
@ -341,28 +341,35 @@ class Backend_Api(Api):
|
||||||
return redirect(source_url)
|
return redirect(source_url)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
self.match_files = {}
|
||||||
|
|
||||||
@app.route('/search/<search>', methods=['GET'])
|
@app.route('/search/<search>', methods=['GET'])
|
||||||
def find_media(search: str):
|
def find_media(search: str):
|
||||||
search = [secure_filename(chunk.lower()) for chunk in search.split("+")]
|
safe_search = [secure_filename(chunk.lower()) for chunk in search.split("+")]
|
||||||
if not os.access(images_dir, os.R_OK):
|
if not os.access(images_dir, os.R_OK):
|
||||||
return jsonify({"error": {"message": "Not found"}}), 404
|
return jsonify({"error": {"message": "Not found"}}), 404
|
||||||
match_files = {}
|
if search not in self.match_files:
|
||||||
|
self.match_files[search] = {}
|
||||||
for root, _, files in os.walk(images_dir):
|
for root, _, files in os.walk(images_dir):
|
||||||
for file in files:
|
for file in files:
|
||||||
mime_type = is_allowed_extension(file)
|
mime_type = is_allowed_extension(file)
|
||||||
if mime_type is not None:
|
if mime_type is not None:
|
||||||
mime_type = secure_filename(mime_type)
|
mime_type = secure_filename(mime_type)
|
||||||
for tag in search:
|
for tag in safe_search:
|
||||||
if tag in mime_type:
|
if tag in mime_type:
|
||||||
match_files[file] = match_files.get(file, 0) + 1
|
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
|
||||||
break
|
break
|
||||||
for tag in search:
|
for tag in safe_search:
|
||||||
if tag in file.lower():
|
if tag in file.lower():
|
||||||
match_files[file] = match_files.get(file, 0) + 1
|
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
|
||||||
match_files = [file for file, count in match_files.items() if count >= request.args.get("min", len(search))]
|
break
|
||||||
|
match_files = [file for file, count in self.match_files[search].items() if count >= request.args.get("min", len(safe_search))]
|
||||||
if int(request.args.get("skip", 0)) >= len(match_files):
|
if int(request.args.get("skip", 0)) >= len(match_files):
|
||||||
return jsonify({"error": {"message": "Not found"}}), 404
|
return jsonify({"error": {"message": "Not found"}}), 404
|
||||||
if (request.args.get("random", False)):
|
if (request.args.get("random", False)):
|
||||||
|
seed = request.args.get("random")
|
||||||
|
if seed not in ["true", "True", "1"]:
|
||||||
|
random.seed(seed)
|
||||||
return redirect(f"/media/{random.choice(match_files)}"), 302
|
return redirect(f"/media/{random.choice(match_files)}"), 302
|
||||||
return redirect(f"/media/{match_files[int(request.args.get('skip', 0))]}", 302)
|
return redirect(f"/media/{match_files[int(request.args.get('skip', 0))]}", 302)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def get_media_extension(media: str) -> str:
|
||||||
extension = os.path.splitext(path)[1]
|
extension = os.path.splitext(path)[1]
|
||||||
if not extension:
|
if not extension:
|
||||||
extension = os.path.splitext(media)[1]
|
extension = os.path.splitext(media)[1]
|
||||||
if not extension:
|
if not extension or len(extension) > 4:
|
||||||
return ""
|
return ""
|
||||||
if extension[1:] not in EXTENSIONS_MAP:
|
if extension[1:] not in EXTENSIONS_MAP:
|
||||||
raise ValueError(f"Unsupported media extension: {extension} in: {media}")
|
raise ValueError(f"Unsupported media extension: {extension} in: {media}")
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ from .Provider import (
|
||||||
Free2GPT,
|
Free2GPT,
|
||||||
FreeGpt,
|
FreeGpt,
|
||||||
HuggingSpace,
|
HuggingSpace,
|
||||||
G4F,
|
|
||||||
Grok,
|
Grok,
|
||||||
DeepseekAI_JanusPro7b,
|
DeepseekAI_JanusPro7b,
|
||||||
Glider,
|
Glider,
|
||||||
|
|
@ -535,7 +534,7 @@ deepseek_r1 = Model(
|
||||||
janus_pro_7b = VisionModel(
|
janus_pro_7b = VisionModel(
|
||||||
name = DeepseekAI_JanusPro7b.default_model,
|
name = DeepseekAI_JanusPro7b.default_model,
|
||||||
base_provider = 'DeepSeek',
|
base_provider = 'DeepSeek',
|
||||||
best_provider = IterListProvider([DeepseekAI_JanusPro7b, G4F])
|
best_provider = IterListProvider([DeepseekAI_JanusPro7b])
|
||||||
)
|
)
|
||||||
|
|
||||||
### x.ai ###
|
### x.ai ###
|
||||||
|
|
@ -985,7 +984,7 @@ demo_models = {
|
||||||
llama_3_2_11b.name: [llama_3_2_11b, [HuggingChat]],
|
llama_3_2_11b.name: [llama_3_2_11b, [HuggingChat]],
|
||||||
qwen_2_vl_7b.name: [qwen_2_vl_7b, [HuggingFaceAPI]],
|
qwen_2_vl_7b.name: [qwen_2_vl_7b, [HuggingFaceAPI]],
|
||||||
deepseek_r1.name: [deepseek_r1, [HuggingFace, PollinationsAI]],
|
deepseek_r1.name: [deepseek_r1, [HuggingFace, PollinationsAI]],
|
||||||
janus_pro_7b.name: [janus_pro_7b, [HuggingSpace, G4F]],
|
janus_pro_7b.name: [janus_pro_7b, [HuggingSpace]],
|
||||||
command_r.name: [command_r, [HuggingSpace]],
|
command_r.name: [command_r, [HuggingSpace]],
|
||||||
command_r_plus.name: [command_r_plus, [HuggingSpace]],
|
command_r_plus.name: [command_r_plus, [HuggingSpace]],
|
||||||
command_r7b.name: [command_r7b, [HuggingSpace]],
|
command_r7b.name: [command_r7b, [HuggingSpace]],
|
||||||
|
|
|
||||||
|
|
@ -425,9 +425,14 @@ class AsyncAuthedProvider(AsyncGeneratorProvider, AuthFileMixin):
|
||||||
if auth_result is not None:
|
if auth_result is not None:
|
||||||
cache_file.parent.mkdir(parents=True, exist_ok=True)
|
cache_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
cache_file.write_text(json.dumps(auth_result.get_dict()))
|
def toJSON(obj):
|
||||||
except TypeError:
|
if hasattr(obj, "get_dict"):
|
||||||
raise RuntimeError(f"Failed to save: {auth_result.get_dict()}")
|
return obj.get_dict()
|
||||||
|
return str(obj)
|
||||||
|
with cache_file.open("w") as cache_file:
|
||||||
|
json.dump(auth_result, cache_file, default=toJSON)
|
||||||
|
except TypeError as e:
|
||||||
|
raise RuntimeError(f"Failed to save: {auth_result.get_dict()}\n{type(e).__name__}: {e}")
|
||||||
elif cache_file.exists():
|
elif cache_file.exists():
|
||||||
cache_file.unlink()
|
cache_file.unlink()
|
||||||
|
|
||||||
|
|
@ -443,7 +448,9 @@ class AsyncAuthedProvider(AsyncGeneratorProvider, AuthFileMixin):
|
||||||
try:
|
try:
|
||||||
if cache_file.exists():
|
if cache_file.exists():
|
||||||
with cache_file.open("r") as f:
|
with cache_file.open("r") as f:
|
||||||
auth_result = AuthResult(**json.load(f))
|
data = f.read()
|
||||||
|
if data:
|
||||||
|
auth_result = AuthResult(**json.loads(data))
|
||||||
else:
|
else:
|
||||||
raise MissingAuthError
|
raise MissingAuthError
|
||||||
yield from to_sync_generator(cls.create_authed(model, messages, auth_result, **kwargs))
|
yield from to_sync_generator(cls.create_authed(model, messages, auth_result, **kwargs))
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ def to_string(value) -> str:
|
||||||
return "".join([to_string(v) for v in value if v.get("type", "text") == "text"])
|
return "".join([to_string(v) for v in value if v.get("type", "text") == "text"])
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
def render_messages(messages: Messages) -> Iterator:
|
||||||
|
for idx, message in enumerate(messages):
|
||||||
|
if isinstance(message, dict) and isinstance(message.get("content"), list):
|
||||||
|
yield {
|
||||||
|
**message,
|
||||||
|
"content": to_string(message["content"]),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
yield message
|
||||||
|
|
||||||
def format_prompt(messages: Messages, add_special_tokens: bool = False, do_continue: bool = False, include_system: bool = True) -> str:
|
def format_prompt(messages: Messages, add_special_tokens: bool = False, do_continue: bool = False, include_system: bool = True) -> str:
|
||||||
"""
|
"""
|
||||||
Format a series of messages into a single string, optionally adding special tokens.
|
Format a series of messages into a single string, optionally adding special tokens.
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,15 @@ class Sources(ResponseType):
|
||||||
for idx, link in enumerate(self.list)
|
for idx, link in enumerate(self.list)
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
class SourceLink(ResponseType):
|
||||||
|
def __init__(self, title: str, url: str) -> None:
|
||||||
|
self.title = title
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
title = f"[{self.title}]"
|
||||||
|
return f" {format_link(self.url, title)}"
|
||||||
|
|
||||||
class YouTube(HiddenResponse):
|
class YouTube(HiddenResponse):
|
||||||
def __init__(self, ids: List[str]) -> None:
|
def __init__(self, ids: List[str]) -> None:
|
||||||
"""Initialize with a list of YouTube IDs."""
|
"""Initialize with a list of YouTube IDs."""
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,9 @@ async def get_args_from_nodriver(
|
||||||
else:
|
else:
|
||||||
await browser.cookies.set_all(get_cookie_params_from_dict(cookies, url=url, domain=domain))
|
await browser.cookies.set_all(get_cookie_params_from_dict(cookies, url=url, domain=domain))
|
||||||
page = await browser.get(url)
|
page = await browser.get(url)
|
||||||
user_agent = str(await page.evaluate("window.navigator.userAgent"))
|
user_agent = await page.evaluate("window.navigator.userAgent", return_by_value=True)
|
||||||
await page.wait_for("body:not(.no-js)", timeout=timeout)
|
while not await page.evaluate("document.querySelector('body:not(.no-js)')"):
|
||||||
|
await asyncio.sleep(1)
|
||||||
if wait_for is not None:
|
if wait_for is not None:
|
||||||
await page.wait_for(wait_for, timeout=timeout)
|
await page.wait_for(wait_for, timeout=timeout)
|
||||||
if callback is not None:
|
if callback is not None:
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ async def raise_for_status_async(response: Union[StreamResponse, ClientResponse]
|
||||||
if response.status == 403 and is_cloudflare(message):
|
if response.status == 403 and is_cloudflare(message):
|
||||||
raise CloudflareError(f"Response {response.status}: Cloudflare detected")
|
raise CloudflareError(f"Response {response.status}: Cloudflare detected")
|
||||||
elif response.status == 403 and is_openai(message):
|
elif response.status == 403 and is_openai(message):
|
||||||
raise ResponseStatusError(f"Response {response.status}: OpenAI Bot detected")
|
raise MissingAuthError(f"Response {response.status}: OpenAI Bot detected")
|
||||||
elif response.status == 502:
|
elif response.status == 502:
|
||||||
raise ResponseStatusError(f"Response {response.status}: Bad Gateway")
|
raise ResponseStatusError(f"Response {response.status}: Bad Gateway")
|
||||||
elif response.status == 504:
|
elif response.status == 504:
|
||||||
|
|
@ -71,7 +71,7 @@ def raise_for_status(response: Union[Response, StreamResponse, ClientResponse, R
|
||||||
if response.status_code == 403 and is_cloudflare(response.text):
|
if response.status_code == 403 and is_cloudflare(response.text):
|
||||||
raise CloudflareError(f"Response {response.status_code}: Cloudflare detected")
|
raise CloudflareError(f"Response {response.status_code}: Cloudflare detected")
|
||||||
elif response.status_code == 403 and is_openai(response.text):
|
elif response.status_code == 403 and is_openai(response.text):
|
||||||
raise ResponseStatusError(f"Response {response.status_code}: OpenAI Bot detected")
|
raise MissingAuthError(f"Response {response.status_code}: OpenAI Bot detected")
|
||||||
elif response.status_code == 502:
|
elif response.status_code == 502:
|
||||||
raise ResponseStatusError(f"Response {response.status_code}: Bad Gateway")
|
raise ResponseStatusError(f"Response {response.status_code}: Bad Gateway")
|
||||||
elif response.status_code == 504:
|
elif response.status_code == 504:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue