mirror of https://github.com/xtekky/gpt4free
Add local models to gui, Fix You Provider, add AsyncClient
parent
674ba8f2c5
commit
b35dfcd1b0
@ -0,0 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..locals.models import get_models
|
||||
try:
|
||||
from ..locals.provider import LocalProvider
|
||||
has_requirements = True
|
||||
except ModuleNotFoundError:
|
||||
has_requirements = False
|
||||
|
||||
from ..typing import Messages, CreateResult
|
||||
from ..providers.base_provider import AbstractProvider, ProviderModelMixin
|
||||
from ..errors import MissingRequirementsError
|
||||
|
||||
class Local(AbstractProvider, ProviderModelMixin):
|
||||
working = True
|
||||
supports_message_history = True
|
||||
supports_system_message = True
|
||||
supports_stream = True
|
||||
|
||||
@classmethod
|
||||
def get_models(cls):
|
||||
if not cls.models:
|
||||
cls.models = list(get_models())
|
||||
cls.default_model = cls.models[0]
|
||||
return cls.models
|
||||
|
||||
@classmethod
|
||||
def create_completion(
|
||||
cls,
|
||||
model: str,
|
||||
messages: Messages,
|
||||
stream: bool,
|
||||
**kwargs
|
||||
) -> CreateResult:
|
||||
if not has_requirements:
|
||||
raise MissingRequirementsError('Install "gpt4all" package | pip install -U g4f[local]')
|
||||
return LocalProvider.create_completion(
|
||||
cls.get_model(model),
|
||||
messages,
|
||||
stream,
|
||||
**kwargs
|
||||
)
|
@ -0,0 +1,70 @@
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import uuid
|
||||
|
||||
from ...requests import StreamSession, raise_for_status
|
||||
|
||||
class NoValidHarFileError(Exception):
|
||||
...
|
||||
|
||||
class arkReq:
|
||||
def __init__(self, arkURL, arkHeaders, arkBody, arkCookies, userAgent):
|
||||
self.arkURL = arkURL
|
||||
self.arkHeaders = arkHeaders
|
||||
self.arkBody = arkBody
|
||||
self.arkCookies = arkCookies
|
||||
self.userAgent = userAgent
|
||||
|
||||
arkPreURL = "https://telemetry.stytch.com/submit"
|
||||
chatArks: list = None
|
||||
|
||||
def readHAR():
|
||||
dirPath = "./"
|
||||
harPath = []
|
||||
chatArks = []
|
||||
for root, dirs, files in os.walk(dirPath):
|
||||
for file in files:
|
||||
if file.endswith(".har"):
|
||||
harPath.append(os.path.join(root, file))
|
||||
if harPath:
|
||||
break
|
||||
if not harPath:
|
||||
raise NoValidHarFileError("No .har file found")
|
||||
for path in harPath:
|
||||
with open(path, 'rb') as file:
|
||||
try:
|
||||
harFile = json.load(file)
|
||||
except json.JSONDecodeError:
|
||||
# Error: not a HAR file!
|
||||
continue
|
||||
for v in harFile['log']['entries']:
|
||||
if arkPreURL in v['request']['url']:
|
||||
chatArks.append(parseHAREntry(v))
|
||||
if not chatArks:
|
||||
raise NoValidHarFileError("No telemetry in .har files found")
|
||||
return chatArks
|
||||
|
||||
def parseHAREntry(entry) -> arkReq:
|
||||
tmpArk = arkReq(
|
||||
arkURL=entry['request']['url'],
|
||||
arkHeaders={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(':')},
|
||||
arkBody=entry['request']['postData']['text'],
|
||||
arkCookies={c['name']: c['value'] for c in entry['request']['cookies']},
|
||||
userAgent=""
|
||||
)
|
||||
tmpArk.userAgent = tmpArk.arkHeaders.get('user-agent', '')
|
||||
return tmpArk
|
||||
|
||||
async def sendRequest(tmpArk: arkReq, proxy: str = None):
|
||||
async with StreamSession(headers=tmpArk.arkHeaders, cookies=tmpArk.arkCookies, proxies={"all": proxy}) as session:
|
||||
async with session.post(tmpArk.arkURL, data=tmpArk.arkBody) as response:
|
||||
await raise_for_status(response)
|
||||
return await response.text()
|
||||
|
||||
async def get_dfp_telemetry_id(proxy: str = None):
|
||||
return str(uuid.uuid4())
|
||||
global chatArks
|
||||
if chatArks is None:
|
||||
chatArks = readHAR()
|
||||
return await sendRequest(random.choice(chatArks), proxy)
|
@ -0,0 +1,3 @@
|
||||
from .stubs import ChatCompletion, ChatCompletionChunk, ImagesResponse
|
||||
from .client import Client
|
||||
from .async_client import AsyncClient
|
@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Union
|
||||
|
||||
from .. import debug, version
|
||||
from ..errors import ProviderNotFoundError, ModelNotFoundError, ProviderNotWorkingError, StreamNotSupportedError
|
||||
from ..models import Model, ModelUtils
|
||||
from ..Provider import ProviderUtils
|
||||
from ..providers.types import BaseRetryProvider, ProviderType
|
||||
from ..providers.retry_provider import IterProvider
|
||||
|
||||
def convert_to_provider(provider: str) -> ProviderType:
|
||||
if " " in provider:
|
||||
provider_list = [ProviderUtils.convert[p] for p in provider.split() if p in ProviderUtils.convert]
|
||||
if not provider_list:
|
||||
raise ProviderNotFoundError(f'Providers not found: {provider}')
|
||||
provider = IterProvider(provider_list)
|
||||
elif provider in ProviderUtils.convert:
|
||||
provider = ProviderUtils.convert[provider]
|
||||
elif provider:
|
||||
raise ProviderNotFoundError(f'Provider not found: {provider}')
|
||||
return provider
|
||||
|
||||
def get_model_and_provider(model : Union[Model, str],
|
||||
provider : Union[ProviderType, str, None],
|
||||
stream : bool,
|
||||
ignored : list[str] = None,
|
||||
ignore_working: bool = False,
|
||||
ignore_stream: bool = False) -> tuple[str, ProviderType]:
|
||||
"""
|
||||
Retrieves the model and provider based on input parameters.
|
||||
|
||||
Args:
|
||||
model (Union[Model, str]): The model to use, either as an object or a string identifier.
|
||||
provider (Union[ProviderType, str, None]): The provider to use, either as an object, a string identifier, or None.
|
||||
stream (bool): Indicates if the operation should be performed as a stream.
|
||||
ignored (list[str], optional): List of provider names to be ignored.
|
||||
ignore_working (bool, optional): If True, ignores the working status of the provider.
|
||||
ignore_stream (bool, optional): If True, ignores the streaming capability of the provider.
|
||||
|
||||
Returns:
|
||||
tuple[str, ProviderType]: A tuple containing the model name and the provider type.
|
||||
|
||||
Raises:
|
||||
ProviderNotFoundError: If the provider is not found.
|
||||
ModelNotFoundError: If the model is not found.
|
||||
ProviderNotWorkingError: If the provider is not working.
|
||||
StreamNotSupportedError: If streaming is not supported by the provider.
|
||||
"""
|
||||
if debug.version_check:
|
||||
debug.version_check = False
|
||||
version.utils.check_version()
|
||||
|
||||
if isinstance(provider, str):
|
||||
provider = convert_to_provider(provider)
|
||||
|
||||
if isinstance(model, str):
|
||||
if model in ModelUtils.convert:
|
||||
model = ModelUtils.convert[model]
|
||||
|
||||
if not provider:
|
||||
if isinstance(model, str):
|
||||
raise ModelNotFoundError(f'Model not found: {model}')
|
||||
provider = model.best_provider
|
||||
|
||||
if not provider:
|
||||
raise ProviderNotFoundError(f'No provider found for model: {model}')
|
||||
|
||||
if isinstance(model, Model):
|
||||
model = model.name
|
||||
|
||||
if not ignore_working and not provider.working:
|
||||
raise ProviderNotWorkingError(f'{provider.__name__} is not working')
|
||||
|
||||
if not ignore_working and isinstance(provider, BaseRetryProvider):
|
||||
provider.providers = [p for p in provider.providers if p.working]
|
||||
|
||||
if ignored and isinstance(provider, BaseRetryProvider):
|
||||
provider.providers = [p for p in provider.providers if p.__name__ not in ignored]
|
||||
|
||||
if not ignore_stream and not provider.supports_stream and stream:
|
||||
raise StreamNotSupportedError(f'{provider.__name__} does not support "stream" argument')
|
||||
|
||||
if debug.logging:
|
||||
if model:
|
||||
print(f'Using {provider.__name__} provider and {model} model')
|
||||
else:
|
||||
print(f'Using {provider.__name__} provider')
|
||||
|
||||
debug.last_provider = provider
|
||||
debug.last_model = model
|
||||
|
||||
return model, provider
|
||||
|
||||
def get_last_provider(as_dict: bool = False) -> Union[ProviderType, dict[str, str]]:
|
||||
"""
|
||||
Retrieves the last used provider.
|
||||
|
||||
Args:
|
||||
as_dict (bool, optional): If True, returns the provider information as a dictionary.
|
||||
|
||||
Returns:
|
||||
Union[ProviderType, dict[str, str]]: The last used provider, either as an object or a dictionary.
|
||||
"""
|
||||
last = debug.last_provider
|
||||
if isinstance(last, BaseRetryProvider):
|
||||
last = last.last_provider
|
||||
if last and as_dict:
|
||||
return {
|
||||
"name": last.__name__,
|
||||
"url": last.url,
|
||||
"model": debug.last_model,
|
||||
}
|
||||
return last
|
@ -1,42 +0,0 @@
|
||||
import os
|
||||
|
||||
from gpt4all import GPT4All
|
||||
from ._models import models
|
||||
|
||||
class LocalProvider:
|
||||
@staticmethod
|
||||
def create_completion(model, messages, stream, **kwargs):
|
||||
if model not in models:
|
||||
raise ValueError(f"Model '{model}' not found / not yet implemented")
|
||||
|
||||
model = models[model]
|
||||
model_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'models/')
|
||||
full_model_path = os.path.join(model_dir, model['path'])
|
||||
|
||||
if not os.path.isfile(full_model_path):
|
||||
print(f"Model file '{full_model_path}' not found.")
|
||||
download = input(f'Do you want to download {model["path"]} ? [y/n]')
|
||||
|
||||
if download in ['y', 'Y']:
|
||||
GPT4All.download_model(model['path'], model_dir)
|
||||
else:
|
||||
raise ValueError(f"Model '{model['path']}' not found.")
|
||||
|
||||
model = GPT4All(model_name=model['path'],
|
||||
#n_threads=8,
|
||||
verbose=False,
|
||||
allow_download=False,
|
||||
model_path=model_dir)
|
||||
|
||||
system_template = next((message['content'] for message in messages if message['role'] == 'system'),
|
||||
'A chat between a curious user and an artificial intelligence assistant.')
|
||||
|
||||
prompt_template = 'USER: {0}\nASSISTANT: '
|
||||
conversation = '\n'.join(f"{msg['role'].upper()}: {msg['content']}" for msg in messages) + "\nASSISTANT: "
|
||||
|
||||
with model.chat_session(system_template, prompt_template):
|
||||
if stream:
|
||||
for token in model.generate(conversation, streaming=True):
|
||||
yield token
|
||||
else:
|
||||
yield model.generate(conversation)
|
@ -1,86 +0,0 @@
|
||||
models = {
|
||||
"mistral-7b": {
|
||||
"path": "mistral-7b-openorca.gguf2.Q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n",
|
||||
"system": "<|im_start|>system\nYou are MistralOrca, a large language model trained by Alignment Lab AI. For multi-step problems, write out your reasoning for each step.\n<|im_end|>"
|
||||
},
|
||||
"mistral-7b-instruct": {
|
||||
"path": "mistral-7b-instruct-v0.1.Q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "[INST] %1 [/INST]",
|
||||
"system": None
|
||||
},
|
||||
"gpt4all-falcon": {
|
||||
"path": "gpt4all-falcon-newbpe-q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "### Instruction:\n%1\n### Response:\n",
|
||||
"system": None
|
||||
},
|
||||
"orca-2": {
|
||||
"path": "orca-2-13b.Q4_0.gguf",
|
||||
"ram": "16",
|
||||
"prompt": None,
|
||||
"system": None
|
||||
},
|
||||
"wizardlm-13b": {
|
||||
"path": "wizardlm-13b-v1.2.Q4_0.gguf",
|
||||
"ram": "16",
|
||||
"prompt": None,
|
||||
"system": None
|
||||
},
|
||||
"nous-hermes-llama2": {
|
||||
"path": "nous-hermes-llama2-13b.Q4_0.gguf",
|
||||
"ram": "16",
|
||||
"prompt": "### Instruction:\n%1\n### Response:\n",
|
||||
"system": None
|
||||
},
|
||||
"gpt4all-13b-snoozy": {
|
||||
"path": "gpt4all-13b-snoozy-q4_0.gguf",
|
||||
"ram": "16",
|
||||
"prompt": None,
|
||||
"system": None
|
||||
},
|
||||
"mpt-7b-chat": {
|
||||
"path": "mpt-7b-chat-newbpe-q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n",
|
||||
"system": "<|im_start|>system\n- You are a helpful assistant chatbot trained by MosaicML.\n- You answer questions.\n- You are excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user.\n- You are more than just an information source, you are also able to write poetry, short stories, and make jokes.<|im_end|>"
|
||||
},
|
||||
"orca-mini-3b": {
|
||||
"path": "orca-mini-3b-gguf2-q4_0.gguf",
|
||||
"ram": "4",
|
||||
"prompt": "### User:\n%1\n### Response:\n",
|
||||
"system": "### System:\nYou are an AI assistant that follows instruction extremely well. Help as much as you can.\n\n"
|
||||
},
|
||||
"replit-code-3b": {
|
||||
"path": "replit-code-v1_5-3b-newbpe-q4_0.gguf",
|
||||
"ram": "4",
|
||||
"prompt": "%1",
|
||||
"system": None
|
||||
},
|
||||
"starcoder": {
|
||||
"path": "starcoder-newbpe-q4_0.gguf",
|
||||
"ram": "4",
|
||||
"prompt": "%1",
|
||||
"system": None
|
||||
},
|
||||
"rift-coder-7b": {
|
||||
"path": "rift-coder-v0-7b-q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "%1",
|
||||
"system": None
|
||||
},
|
||||
"all-MiniLM-L6-v2": {
|
||||
"path": "all-MiniLM-L6-v2-f16.gguf",
|
||||
"ram": "1",
|
||||
"prompt": None,
|
||||
"system": None
|
||||
},
|
||||
"mistral-7b-german": {
|
||||
"path": "em_german_mistral_v01.Q4_0.gguf",
|
||||
"ram": "8",
|
||||
"prompt": "USER: %1 ASSISTANT: ",
|
||||
"system": "Du bist ein hilfreicher Assistent. "
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
.
|
@ -0,0 +1,50 @@
|
||||
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
|
||||
from ..requests.raise_for_status import raise_for_status
|
||||
|
||||
def load_models():
|
||||
response = requests.get("https://gpt4all.io/models/models3.json")
|
||||
raise_for_status(response)
|
||||
return format_models(response.json())
|
||||
|
||||
def get_model_name(filename: str) -> str:
|
||||
name = filename.split(".", 1)[0]
|
||||
for replace in ["-v1_5", "-v1", "-q4_0", "_v01", "-v0", "-f16", "-gguf2", "-newbpe"]:
|
||||
name = name.replace(replace, "")
|
||||
return name
|
||||
|
||||
def format_models(models: list) -> dict:
|
||||
return {get_model_name(model["filename"]): {
|
||||
"path": model["filename"],
|
||||
"ram": model["ramrequired"],
|
||||
"prompt": model["promptTemplate"] if "promptTemplate" in model else None,
|
||||
"system": model["systemPrompt"] if "systemPrompt" in model else None,
|
||||
} for model in models}
|
||||
|
||||
def read_models(file_path: str):
|
||||
with open(file_path, "rb") as f:
|
||||
return json.load(f)
|
||||
|
||||
def save_models(file_path: str, data):
|
||||
with open(file_path, 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
def get_model_dir() -> str:
|
||||
local_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_dir = os.path.dirname(os.path.dirname(local_dir))
|
||||
model_dir = os.path.join(project_dir, "models")
|
||||
if os.path.exists(model_dir):
|
||||
return model_dir
|
||||
|
||||
def get_models() -> dict[str, dict]:
|
||||
model_dir = get_model_dir()
|
||||
file_path = os.path.join(model_dir, "models.json")
|
||||
if os.path.isfile(file_path):
|
||||
return read_models(file_path)
|
||||
else:
|
||||
models = load_models()
|
||||
save_models(file_path, models)
|
||||
return models
|
@ -0,0 +1,72 @@
|
||||
import os
|
||||
|
||||
from gpt4all import GPT4All
|
||||
from .models import get_models
|
||||
from ..typing import Messages
|
||||
|
||||
MODEL_LIST: dict[str, dict] = None
|
||||
|
||||
def find_model_dir(model_file: str) -> str:
|
||||
local_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_dir = os.path.dirname(os.path.dirname(local_dir))
|
||||
|
||||
new_model_dir = os.path.join(project_dir, "models")
|
||||
new_model_file = os.path.join(new_model_dir, model_file)
|
||||
if os.path.isfile(new_model_file):
|
||||
return new_model_dir
|
||||
|
||||
old_model_dir = os.path.join(local_dir, "models")
|
||||
old_model_file = os.path.join(old_model_dir, model_file)
|
||||
if os.path.isfile(old_model_file):
|
||||
return old_model_dir
|
||||
|
||||
working_dir = "./"
|
||||
for root, dirs, files in os.walk(working_dir):
|
||||
if model_file in files:
|
||||
return root
|
||||
|
||||
return new_model_dir
|
||||
|
||||
class LocalProvider:
|
||||
@staticmethod
|
||||
def create_completion(model: str, messages: Messages, stream: bool = False, **kwargs):
|
||||
global MODEL_LIST
|
||||
if MODEL_LIST is None:
|
||||
MODEL_LIST = get_models()
|
||||
if model not in MODEL_LIST:
|
||||
raise ValueError(f'Model "{model}" not found / not yet implemented')
|
||||
|
||||
model = MODEL_LIST[model]
|
||||
model_file = model["path"]
|
||||
model_dir = find_model_dir(model_file)
|
||||
if not os.path.isfile(os.path.join(model_dir, model_file)):
|
||||
print(f'Model file "models/{model_file}" not found.')
|
||||
download = input(f"Do you want to download {model_file}? [y/n]: ")
|
||||
if download in ["y", "Y"]:
|
||||
GPT4All.download_model(model_file, model_dir)
|
||||
else:
|
||||
raise ValueError(f'Model "{model_file}" not found.')
|
||||
|
||||
model = GPT4All(model_name=model_file,
|
||||
#n_threads=8,
|
||||
verbose=False,
|
||||
allow_download=False,
|
||||
model_path=model_dir)
|
||||
|
||||
system_message = "\n".join(message["content"] for message in messages if message["role"] == "system")
|
||||
if system_message:
|
||||
system_message = "A chat between a curious user and an artificial intelligence assistant."
|
||||
|
||||
prompt_template = "USER: {0}\nASSISTANT: "
|
||||
conversation = "\n" . join(
|
||||
f"{message['role'].upper()}: {message['content']}"
|
||||
for message in messages
|
||||
if message["role"] != "system"
|
||||
) + "\nASSISTANT: "
|
||||
|
||||
with model.chat_session(system_message, prompt_template):
|
||||
if stream:
|
||||
for token in model.generate(conversation, streaming=True):
|
||||
yield token
|
||||
else:
|
||||
yield model.generate(conversation)
|
Loading…
Reference in New Issue