Skip to content

Commit

Permalink
Merge pull request #2486 from hlohaus/vvv
Browse files Browse the repository at this point in the history
Improved ignored providers support,
  • Loading branch information
hlohaus authored Dec 15, 2024
2 parents ca2b609 + ff66df1 commit f317fea
Show file tree
Hide file tree
Showing 16 changed files with 157 additions and 121 deletions.
1 change: 1 addition & 0 deletions g4f/Provider/Blackbox2.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ async def create_async_generator(
messages: Messages,
prompt: str = None,
proxy: str = None,
prompt: str = None,
max_retries: int = 3,
delay: int = 1,
max_tokens: int = None,
Expand Down
9 changes: 5 additions & 4 deletions g4f/Provider/Copilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ def create_completion(
prompt = format_prompt(messages)
if len(prompt) > 10000:
if len(messages) > 6:
prompt = format_prompt(messages[:3]+messages[-3:])
elif len(messages) > 2:
prompt = format_prompt(messages[:2]+messages[-1:])
prompt = format_prompt(messages[:3] + messages[-3:])
if len(prompt) > 10000:
prompt = messages[-1]["content"]
if len(messages) > 2:
prompt = format_prompt(messages[:2] + messages[-1:])
if len(prompt) > 10000:
prompt = messages[-1]["content"]
debug.log(f"Copilot: Trim messages to: {len(prompt)}")
debug.log(f"Copilot: Created conversation: {conversation_id}")
else:
Expand Down
46 changes: 17 additions & 29 deletions g4f/Provider/DarkAI.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,26 @@
from aiohttp import ClientSession

from ..typing import AsyncResult, Messages
from ..requests.raise_for_status import raise_for_status
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt


class DarkAI(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://darkai.foundation/chat"
api_endpoint = "https://darkai.foundation/chat"
working = True
supports_stream = True
supports_system_message = True
supports_message_history = True


default_model = 'llama-3-70b'
models = [
'gpt-4o', # Uncensored
'gpt-3.5-turbo', # Uncensored
default_model,
]

model_aliases = {
"llama-3.1-70b": "llama-3-70b",
}

@classmethod
def get_model(cls, model: str) -> str:
if model in cls.models:
return model
elif model in cls.model_aliases:
return cls.model_aliases[model]
else:
return cls.default_model

@classmethod
async def create_async_generator(
cls,
Expand All @@ -45,7 +33,7 @@ async def create_async_generator(
**kwargs
) -> AsyncResult:
model = cls.get_model(model)

headers = {
"accept": "text/event-stream",
"content-type": "application/json",
Expand All @@ -58,24 +46,24 @@ async def create_async_generator(
"model": model,
}
async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
response.raise_for_status()
full_text = ""
async for chunk in response.content:
if chunk:
await raise_for_status(response)
first = True
async for line in response.content:
if line:
try:
chunk_str = chunk.decode().strip()
if chunk_str.startswith('data: '):
chunk_data = json.loads(chunk_str[6:])
line_str = line.decode().strip()
if line_str.startswith('data: '):
chunk_data = json.loads(line_str[6:])
if chunk_data['event'] == 'text-chunk':
full_text += chunk_data['data']['text']
chunk = chunk_data['data']['text']
if first:
chunk = chunk.lstrip()
if chunk:
first = False
yield chunk
elif chunk_data['event'] == 'stream-end':
if full_text:
yield full_text.strip()
return
except json.JSONDecodeError:
pass
except Exception:
pass

if full_text:
yield full_text.strip()
pass
23 changes: 4 additions & 19 deletions g4f/Provider/needs_auth/Cerebras.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import requests
from aiohttp import ClientSession

from .OpenaiAPI import OpenaiAPI
Expand All @@ -11,35 +10,21 @@
class Cerebras(OpenaiAPI):
label = "Cerebras Inference"
url = "https://inference.cerebras.ai/"
api_base = "https://api.cerebras.ai/v1"
working = True
default_model = "llama3.1-70b"
fallback_models = [
models = [
"llama3.1-70b",
"llama3.1-8b",
]
model_aliases = {"llama-3.1-70b": "llama3.1-70b", "llama-3.1-8b": "llama3.1-8b"}

@classmethod
def get_models(cls, api_key: str = None):
if not cls.models:
try:
headers = {}
if api_key:
headers["authorization"] = f"Bearer ${api_key}"
response = requests.get(f"https://api.cerebras.ai/v1/models", headers=headers)
raise_for_status(response)
data = response.json()
cls.models = [model.get("model") for model in data.get("models")]
except Exception:
cls.models = cls.fallback_models
return cls.models

@classmethod
async def create_async_generator(
cls,
model: str,
messages: Messages,
api_base: str = "https://api.cerebras.ai/v1",
api_base: str = api_base,
api_key: str = None,
cookies: Cookies = None,
**kwargs
Expand All @@ -62,4 +47,4 @@ async def create_async_generator(
},
**kwargs
):
yield chunk
yield chunk
7 changes: 4 additions & 3 deletions g4f/Provider/needs_auth/Groq.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
class Groq(OpenaiAPI):
label = "Groq"
url = "https://console.groq.com/playground"
api_base = "https://api.groq.com/openai/v1"
working = True
default_model = "mixtral-8x7b-32768"
models = [
fallback_models = [
"distil-whisper-large-v3-en",
"gemma2-9b-it",
"gemma-7b-it",
Expand All @@ -35,9 +36,9 @@ def create_async_generator(
cls,
model: str,
messages: Messages,
api_base: str = "https://api.groq.com/openai/v1",
api_base: str = api_base,
**kwargs
) -> AsyncResult:
return super().create_async_generator(
model, messages, api_base=api_base, **kwargs
)
)
36 changes: 24 additions & 12 deletions g4f/Provider/needs_auth/HuggingFace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import requests

from ...typing import AsyncResult, Messages
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ...errors import ModelNotFoundError, ModelNotSupportedError
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_prompt
from ...errors import ModelNotFoundError, ModelNotSupportedError, ResponseError
from ...requests import StreamSession, raise_for_status
from ...image import ImageResponse

Expand All @@ -28,9 +28,11 @@ def get_models(cls) -> list[str]:
cls.models = [model["id"] for model in requests.get(url).json()]
cls.models.append("meta-llama/Llama-3.2-11B-Vision-Instruct")
cls.models.append("nvidia/Llama-3.1-Nemotron-70B-Instruct-HF")
cls.models.sort()
if not cls.image_models:
url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
cls.image_models = [model["id"] for model in requests.get(url).json() if model["trendingScore"] >= 20]
cls.image_models.sort()
cls.models.extend(cls.image_models)
return cls.models

Expand Down Expand Up @@ -89,19 +91,27 @@ async def create_async_generator(
) as session:
if payload is None:
async with session.get(f"https://huggingface.co/api/models/{model}") as response:
await raise_for_status(response)
model_data = await response.json()
if "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
model_type = None
if "config" in model_data and "model_type" in model_data["config"]:
model_type = model_data["config"]["model_type"]
if model_type in ("gpt2", "gpt_neo", "gemma", "gemma2"):
inputs = format_prompt(messages)
elif "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
eos_token = model_data["config"]["tokenizer_config"]["eos_token"]
if eos_token == "</s>":
inputs = format_prompt_mistral(messages)
if eos_token in ("<|endoftext|>", "<eos>", "</s>"):
inputs = format_prompt_custom(messages, eos_token)
elif eos_token == "<|im_end|>":
inputs = format_prompt_qwen(messages)
elif eos_token == "<|eot_id|>":
inputs = format_prompt_llama(messages)
else:
inputs = format_prompt(messages)
inputs = format_prompt_default(messages)
else:
inputs = format_prompt(messages)
inputs = format_prompt_default(messages)
if model_type == "gpt2" and max_new_tokens >= 1024:
params["max_new_tokens"] = 512
payload = {"inputs": inputs, "parameters": params, "stream": stream}

async with session.post(f"{api_base.rstrip('/')}/models/{model}", json=payload) as response:
Expand All @@ -113,6 +123,8 @@ async def create_async_generator(
async for line in response.iter_lines():
if line.startswith(b"data:"):
data = json.loads(line[5:])
if "error" in data:
raise ResponseError(data["error"])
if not data["token"]["special"]:
chunk = data["token"]["text"]
if first:
Expand All @@ -128,7 +140,7 @@ async def create_async_generator(
else:
yield (await response.json())[0]["generated_text"].strip()

def format_prompt(messages: Messages) -> str:
def format_prompt_default(messages: Messages) -> str:
system_messages = [message["content"] for message in messages if message["role"] == "system"]
question = " ".join([messages[-1]["content"], *system_messages])
history = "".join([
Expand All @@ -146,9 +158,9 @@ def format_prompt_qwen(messages: Messages) -> str:
def format_prompt_llama(messages: Messages) -> str:
return "<|begin_of_text|>" + "".join([
f"<|start_header_id|>{message['role']}<|end_header_id|>\n\n{message['content']}\n<|eot_id|>\n" for message in messages
]) + "<|start_header_id|>assistant<|end_header_id|>\\n\\n"
def format_prompt_mistral(messages: Messages) -> str:
]) + "<|start_header_id|>assistant<|end_header_id|>\n\n"

def format_prompt_custom(messages: Messages, end_token: str = "</s>") -> str:
return "".join([
f"<|{message['role']}|>\n{message['content']}'</s>\n" for message in messages
f"<|{message['role']}|>\n{message['content']}{end_token}\n" for message in messages
]) + "<|assistant|>\n"
3 changes: 2 additions & 1 deletion g4f/Provider/needs_auth/HuggingFaceAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
class HuggingFaceAPI(OpenaiAPI):
label = "HuggingFace (Inference API)"
url = "https://api-inference.huggingface.co"
api_base = "https://api-inference.huggingface.co/v1"
working = True
default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
default_vision_model = default_model
Expand All @@ -19,7 +20,7 @@ def create_async_generator(
cls,
model: str,
messages: Messages,
api_base: str = "https://api-inference.huggingface.co/v1",
api_base: str = api_base,
max_tokens: int = 500,
**kwargs
) -> AsyncResult:
Expand Down
32 changes: 28 additions & 4 deletions g4f/Provider/needs_auth/OpenaiAPI.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
from __future__ import annotations

import json
import requests

from ..helper import filter_none
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, FinishReason
from ...typing import Union, Optional, AsyncResult, Messages, ImagesType
from ...requests import StreamSession, raise_for_status
from ...errors import MissingAuthError, ResponseError
from ...image import to_data_uri
from ... import debug

class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin):
label = "OpenAI API"
url = "https://platform.openai.com"
api_base = "https://api.openai.com/v1"
working = True
needs_auth = True
supports_message_history = True
supports_system_message = True
default_model = ""
fallback_models = []

@classmethod
def get_models(cls, api_key: str = None):
if not cls.models:
try:
headers = {}
if api_key is not None:
headers["authorization"] = f"Bearer {api_key}"
response = requests.get(f"{cls.api_base}/models", headers=headers)
raise_for_status(response)
data = response.json()
cls.models = [model.get("id") for model in data.get("data")]
cls.models.sort()
except Exception as e:
debug.log(e)
cls.models = cls.fallback_models
return cls.models

@classmethod
async def create_async_generator(
Expand All @@ -27,7 +48,7 @@ async def create_async_generator(
timeout: int = 120,
images: ImagesType = None,
api_key: str = None,
api_base: str = "https://api.openai.com/v1",
api_base: str = api_base,
temperature: float = None,
max_tokens: int = None,
top_p: float = None,
Expand All @@ -47,14 +68,14 @@ async def create_async_generator(
*[{
"type": "image_url",
"image_url": {"url": to_data_uri(image)}
} for image, image_name in images],
} for image, _ in images],
{
"type": "text",
"text": messages[-1]["content"]
}
]
async with StreamSession(
proxies={"all": proxy},
proxy=proxy,
headers=cls.get_headers(stream, api_key, headers),
timeout=timeout,
impersonate=impersonate,
Expand Down Expand Up @@ -111,7 +132,10 @@ def raise_error(data: dict):
if "error_message" in data:
raise ResponseError(data["error_message"])
elif "error" in data:
raise ResponseError(f'Error {data["error"]["code"]}: {data["error"]["message"]}')
if "code" in data["error"]:
raise ResponseError(f'Error {data["error"]["code"]}: {data["error"]["message"]}')
else:
raise ResponseError(data["error"]["message"])

@classmethod
def get_headers(cls, stream: bool, api_key: str = None, headers: dict = None) -> dict:
Expand Down
1 change: 1 addition & 0 deletions g4f/Provider/needs_auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
from .Theb import Theb
from .ThebApi import ThebApi
from .WhiteRabbitNeo import WhiteRabbitNeo
from .xAI import xAI
Loading

0 comments on commit f317fea

Please sign in to comment.