diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6e999f --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +### `g4f.Providers` class + +default: + +`./g4f/Providers/ProviderName.py`: +```python +import os + + +url: str = 'https://{site_link}' +model: str = 'gpt-[version]' + +def _create_completion(prompt: str, args...): + return ... + or + yield ... + + +params = f'g4f.Providers.{os.path.basename(__file__)[:-3]} supports: ' + \ + ', '.join([f"{name}: {get_type_hints(_create_completion)[name].__name__}" for name in _create_completion.__code__.co_varnames[:_create_completion.__code__.co_argcount]]) +``` \ No newline at end of file diff --git a/g4f/Providers/Ails.py b/g4f/Providers/Ails.py new file mode 100644 index 0000000..d968ca8 --- /dev/null +++ b/g4f/Providers/Ails.py @@ -0,0 +1,70 @@ +import os +import time +import json +import uuid +import hashlib +import requests + +from ..typing import sha256, Dict, get_type_hints +from datetime import datetime + +url: str = 'https://ai.ls' +model: str = 'gpt-3.5-turbo' + + +class Utils: + def hash(json_data: Dict[str, str]) -> sha256: + + secretKey: bytearray = bytearray([79, 86, 98, 105, 91, 84, 80, 78, 123, 83, + 35, 41, 99, 123, 51, 54, 37, 57, 63, 103, 59, 117, 115, 108, 41, 67, 76]) + + base_string: str = '%s:%s:%s:%s' % ( + json_data['t'], + json_data['m'], + secretKey.decode(), + len(json_data['m']) + ) + + return hashlib.sha256(base_string.encode()).hexdigest() + + def format_timestamp(timestamp: int) -> str: + + e = timestamp + n = e % 10 + r = n + 1 if n % 2 == 0 else n + return str(e - n + r) + + +def _create_completion(model: str,messages: list, temperature: float = 0.6, stream: bool = False): + headers = { + 'authority': 'api.caipacity.com', + 'authorization': f'Bearer free', + 'client-id': str(uuid.uuid4()), + 'content-type': 'application/json', + 'origin': 'https://ai.ls', + 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' + } + + timestamp = Utils.format_timestamp(int(time.time() * 1000)) + + sig = { + 'd': datetime.now().strftime('%Y-%m-%d'), + 't': timestamp, + 's': Utils.hash({ + 't': timestamp, + 'm': messages[-1]['content']})} + + json_data = json.dumps(separators=(',', ':'), obj={ + 'model': 'gpt-3.5-turbo', + 'temperature': temperature, + 'stream': True, + 'messages': messages} | sig) + + response = requests.post('https://api.caipacity.com/v1/chat/completions?full=false', + headers=headers, data=json_data, stream=True) + + for token in response.iter_lines(): + yield token.decode() + +params = f'g4f.Providers.{os.path.basename(__file__)[:-3]} supports: ' + \ + '(%s)' % ', '.join([f"{name}: {get_type_hints(_create_completion)[name].__name__}" for name in _create_completion.__code__.co_varnames[:_create_completion.__code__.co_argcount]]) \ No newline at end of file diff --git a/g4f/Providers/Phind.py b/g4f/Providers/Phind.py new file mode 100644 index 0000000..50efbc1 --- /dev/null +++ b/g4f/Providers/Phind.py @@ -0,0 +1,29 @@ +import os +import json +import time +import subprocess + +url = None +model = None + +def _create_completion(model: str, messages: list, **kwargs): + + path = os.path.dirname(os.path.realpath(__file__)) + config = json.dumps({ + 'messages': messages}, separators=(',', ':')) + + cmd = ['python3', f'{path}/helpers/phind.py', config] + + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + for line in iter(p.stdout.readline, b''): + if b'Just a moment...' in line: + os.system('clear' if os.name == 'posix' else 'cls') + yield 'Clouflare error, please try again...' + os._exit(0) + + else: + if b'ping - 2023-' in line: + continue + + yield line.decode('utf-8')[:-1] \ No newline at end of file diff --git a/g4f/Providers/Provider.py b/g4f/Providers/Provider.py new file mode 100644 index 0000000..74d53f0 --- /dev/null +++ b/g4f/Providers/Provider.py @@ -0,0 +1,5 @@ +url = None +model = None + +def _create_completion(model: str, messages: list, **kwargs): + return \ No newline at end of file diff --git a/g4f/Providers/Test.py b/g4f/Providers/Test.py new file mode 100644 index 0000000..153fa60 --- /dev/null +++ b/g4f/Providers/Test.py @@ -0,0 +1,2 @@ +def _create_completion(prompt: str, **kwargs): + return 'helloooo', prompt \ No newline at end of file diff --git a/g4f/Providers/__init__.py b/g4f/Providers/__init__.py new file mode 100644 index 0000000..34ed9b1 --- /dev/null +++ b/g4f/Providers/__init__.py @@ -0,0 +1 @@ +from . import Ails, Phind, Test, Provider \ No newline at end of file diff --git a/g4f/Providers/helpers/phind.py b/g4f/Providers/helpers/phind.py new file mode 100644 index 0000000..8997646 --- /dev/null +++ b/g4f/Providers/helpers/phind.py @@ -0,0 +1,53 @@ +import sys +import json +import datetime +import urllib.parse + +from curl_cffi import requests + +config = json.loads(sys.argv[1]) +prompt = config['messages'][-1]['content'] + +json_data = json.dumps({ + 'question': prompt, + 'options': { + 'skill': 'expert', + 'date': datetime.datetime.now().strftime('%d/%m/%Y'), + 'language': 'en', + 'detailed': False, + 'creative': True, + 'customLinks': []}}, separators=(',', ':')) + +headers = { + 'Content-Type': 'application/json', + 'Pragma': 'no-cache', + 'Accept': '*/*', + 'Sec-Fetch-Site': 'same-origin', + 'Accept-Language': 'en-GB,en;q=0.9', + 'Cache-Control': 'no-cache', + 'Sec-Fetch-Mode': 'cors', + 'Content-Length': str(len(json_data)), + 'Origin': 'https://www.phind.com', + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15', + 'Referer': f'https://www.phind.com/search?q={urllib.parse.quote(prompt)}&source=searchbox', + 'Connection': 'keep-alive', + 'Host': 'www.phind.com', + 'Sec-Fetch-Dest': 'empty' +} + + +def output(chunk): + if chunk == b'data: \r\ndata: \r\ndata: \r\n\r\n': + chunk = b'data: \n\r\n\r\n' + + chunk = chunk.decode() + + chunk = chunk.replace('data: \r\n\r\ndata: ', 'data: \n') + chunk = chunk.replace('\r\ndata: \r\ndata: \r\n\r\n', '\n\r\n\r\n') + chunk = chunk.replace('data: ', '').replace('\r\n\r\n', '') + + print(chunk, flush=True) + + +response = requests.post('https://www.phind.com/api/infer/creative', + headers=headers, data=json_data, content_callback=output, timeout=999999, impersonate='safari15_5') diff --git a/g4f/__init__.py b/g4f/__init__.py new file mode 100644 index 0000000..8da364a --- /dev/null +++ b/g4f/__init__.py @@ -0,0 +1,47 @@ +import sys + +from .typing import MetaModels, Union +from . import Providers + + +class Models(metaclass=MetaModels): + + class model: + name: str + base_provider: str + best_site: str + + class gpt_35_turbo: + name: str = 'gpt-3.5-turbo' + base_provider: str = 'openai' + best_site: str = Providers.Ails + + class gpt_4: + name: str = 'gpt-4' + base_provider: str = 'openai' + best_site: str = Providers.Phind + +class Utils: + convert: dict = { + 'gpt-3.5-turbo': Models.gpt_35_turbo, + 'gpt-4': Models.gpt_4 + } + +class ChatCompletion: + @staticmethod + def create(model: Models.model or str, messages: list, provider: Providers.Provider = None, stream: bool = False, **kwargs): + try: + if isinstance(model, str): + model = Utils.convert[model] + + engine = model.best_site if not provider else provider + + return (engine._create_completion(messages, **kwargs) + if stream else ''.join(engine._create_completion(messages, **kwargs))) + + except TypeError as e: + print(e) + arg: str = str(e).split("'")[1] + print( + f"ValueError: {engine.__name__} does not support '{arg}' argument", file=sys.stderr) + sys.exit(1) diff --git a/g4f/typing.py b/g4f/typing.py new file mode 100644 index 0000000..977e821 --- /dev/null +++ b/g4f/typing.py @@ -0,0 +1,16 @@ +from typing import Dict, NewType, Union, Optional, List, get_type_hints + +sha256 = NewType('sha_256_hash', str) + +class MetaModels(type): + def __str__(cls): + output: List = [ + f'class Engines:\n', + f' class {cls.gpt_35_turbo.__name__}:', + ' ...', + f' class {cls.gpt_4.__name__}:', + ' ...' + ] + + return '\n'.join(output) + \ No newline at end of file diff --git a/test b/test deleted file mode 100644 index 30d74d2..0000000 --- a/test +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/testing/interference_test.py b/testing/interference_test.py new file mode 100644 index 0000000..cd02625 --- /dev/null +++ b/testing/interference_test.py @@ -0,0 +1,15 @@ +import openai + +openai.api_key = '' +openai.api_base = 'paste_address_here' + +chat_completion = openai.ChatCompletion.create(stream=True, + model='gpt-3.5-turbo', messages=[{'role': 'user', 'content': 'write a poem about a tree'}]) + +#print(chat_completion.choices[0].message.content) + +for token in chat_completion: + + content = token['choices'][0]['delta'].get('content') + if content != None: + print(content) \ No newline at end of file diff --git a/testing/main_test.py b/testing/main_test.py new file mode 100644 index 0000000..ce75006 --- /dev/null +++ b/testing/main_test.py @@ -0,0 +1,16 @@ +import g4f + + +# print(g4f.Providers.Ails.params) + +response = g4f.ChatCompletion.create(model='gpt-3.5-turbo', provider=g4f.Providers.Phind, messages=[ + {"role": "user", "content": "Hello world"}], stream=True) + +for message in response: + print(message) + + +# response = g4f.ChatCompletion.create(model=g4f.Models.gpt_35_turbo, +# provider=g4f.Providers.Ails, prompt='hi') + +# print(response)