get gpt service from balshdocker (#16)

This commit is contained in:
Dmitry Afanasyev 2023-09-28 17:36:46 +03:00 committed by GitHub
parent 59363a495f
commit 61f3a351e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 379 additions and 2184 deletions

View File

@ -37,8 +37,8 @@ async def about_bot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
return None
await update.effective_message.reply_text(
"Бот использует бесплатную модель Chat-GPT3.5 для ответов на вопросы. "
"Принимает запросы на разных языках. \n\nБот так же умеет переводить голосовые сообщения в текст"
"просто пришлите голосовуху и получите поток сознания без запятых в виде текста",
"Принимает запросы на разных языках. \n\nБот так же умеет переводить голосовые сообщения в текст."
"Просто пришлите голосовуху и получите поток сознания без запятых в виде текста",
parse_mode='Markdown',
)

View File

@ -1,4 +1,4 @@
name: ubuntu-gcc13
name: build
on:
push:
@ -48,12 +48,7 @@ jobs:
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build the Docker image to dev
if: github.ref_name == 'dev'
run: |
docker build . -t ${{ secrets.DOCKERHUB_USERNAME }}/freegpt:dev
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build the Docker image to main
if: github.ref_name == 'main'

View File

@ -25,4 +25,4 @@ RUN ls /app/cfg
WORKDIR /app/bin
ENTRYPOINT ["sh", "-c", "./cpp-freegpt-webui ../cfg/cpp-free-gpt.yml"]
ENTRYPOINT ["sh", "-c", "./cpp-freegpt-webui ../cfg/cpp-free-gpt.yml"]

View File

@ -40,7 +40,7 @@ export LIBRARY_PATH=/usr/lib64:$LIBRARY_PATH
4. Compiling
git clone https://github.com/fantasy-peak/cpp-freegpt-webui.git
cd cpp-freegpt-webui
xmake build -v
xmake build -v -y
xmake install -o .
cd bin
./cpp-freegpt-webui ../cfg/cpp-free-gpt.yml
@ -59,9 +59,9 @@ docker pull fantasypeak/freegpt:latest
Run the application using Docker:
```
docker run --net=host -it --name freegpt fantasypeak/freegpt:latest
// OR
docker run -p 8858:8858 -it --name freegpt fantasypeak/freegpt:latest
// OR
docker run --net=host -it --name freegpt fantasypeak/freegpt:latest
// use http_proxy
docker run -p 8858:8858 -it --name freegpt -e HTTP_PROXY=http://127.0.0.1:8080 -e CHAT_PATH=/chat fantasypeak/freegpt:latest
// set active providers
@ -119,4 +119,4 @@ Please note the following:
By using this repository or any code related to it, you agree to these terms. The author is not responsible for any
copies, forks, or reuploads made by other users. This is the author's only account and repository. To prevent
impersonation or irresponsible actions, you may comply with the GNU GPL license this Repository uses.
impersonation or irresponsible actions, you may comply with the GNU GPL license this Repository uses

View File

@ -9,18 +9,18 @@
<meta property="og:image" content="https://openai.com/content/images/2022/11/ChatGPT.jpg">
<meta property="og:description" content="A conversational AI system that listens, learns, and challenges">
<meta property="og:url" content="https://chat.acy.dev">
<link rel="stylesheet" href="/assets/css/style.css">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon-16x16.png">
<link rel="manifest" href="/assets/img/site.webmanifest">
<script src="/assets/js/icons.js"></script>
<script src="/assets/js/chat.js" defer></script>
<link rel="stylesheet" href="{{chat_path}}/assets/css/style.css">
<link rel="apple-touch-icon" sizes="180x180" href="{{chat_path}}/assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{chat_path}}/assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{chat_path}}/assets/img/favicon-16x16.png">
<link rel="manifest" href="{{chat_path}}/assets/img/site.webmanifest">
<script src="{{chat_path}}/assets/js/icons.js"></script>
<script src="{{chat_path}}/assets/js/chat.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@latest/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/styles/base16/dracula.min.css">
<script>
const user_image = `<img src="/assets/img/user.png" alt="User Avatar">`;
const gpt_image = `<img src="/assets/img/gpt.png" alt="GPT Avatar">`;
const user_image = `<img src="{{chat_path}}/assets/img/user.png" alt="User Avatar">`;
const gpt_image = `<img src="{{chat_path}}/assets/img/gpt.png" alt="GPT Avatar">`;
</script>
<style>
.hljs {
@ -42,21 +42,21 @@
/* Track */
#message-input::-webkit-scrollbar-track {
background: #f1f1f1;
background: #f1f1f1;
}
/* Handle */
#message-input::-webkit-scrollbar-thumb {
background: #c7a2ff;
background: #c7a2ff;
}
/* Handle on hover */
#message-input::-webkit-scrollbar-thumb:hover {
background: #8b3dff;
background: #8b3dff;
}
</style>
<script src="/assets/js/highlight.min.js"></script>
<script src="/assets/js/highlightjs-copy.min.js"></script>
<script src="{{chat_path}}/assets/js/highlight.min.js"></script>
<script src="{{chat_path}}/assets/js/highlightjs-copy.min.js"></script>
<script>window.conversation_id = {{chat_id}} </script>
<title>ChatGPT</title>
</head>
@ -79,8 +79,8 @@
<div class="info">
<i class="fa-regular fa-circle-info"></i>
<span class="convo-title">By: Balsh<br>
Version: 0.0.6 <br>
Release: 2023-09-06<br>
Version: 0.0.7 <br>
Release: 2023-09-28<br>
</span>
</div>
</div>

View File

@ -3,12 +3,12 @@
"short_name": "",
"icons": [
{
"src": "/assets/img/android-chrome-192x192.png",
"src": "{{chat_path}}/assets/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/img/android-chrome-512x512.png",
"src": "{{chat_path}}/assets/img/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}

View File

@ -478,7 +478,7 @@ window.onload = async () => {
}, 1);
if (!window.location.href.endsWith(`#`)) {
if (/\{{chat_path}}\/.+/.test(window.location.href)) {
if (/{{chat_path}}\/.+/.test(window.location.href)) {
await load_conversation(window.conversation_id);
}
}

View File

@ -33,9 +33,9 @@ import re
import subprocess
import sys
usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]'
usage = "git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]"
desc = '''
desc = """
If zero or one commits are given, run clang-format on all lines that differ
between the working directory and <commit>, which defaults to HEAD. Changes are
only applied to the working directory.
@ -48,14 +48,14 @@ The following git-config settings set the default of the corresponding option:
clangFormat.commit
clangFormat.extension
clangFormat.style
'''
"""
# Name of the temporary index file in which save the output of clang-format.
# This file is created within the .git directory.
temp_index_basename = 'clang-format-index'
temp_index_basename = "clang-format-index"
Range = collections.namedtuple('Range', 'start, count')
Range = collections.namedtuple("Range", "start, count")
def main():
@ -66,61 +66,87 @@ def main():
# nargs=argparse.REMAINDER disallows options after positionals.)
argv = sys.argv[1:]
try:
idx = argv.index('--')
idx = argv.index("--")
except ValueError:
dash_dash = []
else:
dash_dash = argv[idx:]
argv = argv[:idx]
default_extensions = ','.join(
default_extensions = ",".join(
[
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
'c',
'h', # C
'm', # ObjC
'mm', # ObjC++
'cc',
'cp',
'cpp',
'c++',
'cxx',
'hh',
'hpp',
'hxx', # C++
'cu', # CUDA
"c",
"h", # C
"m", # ObjC
"mm", # ObjC++
"cc",
"cp",
"cpp",
"c++",
"cxx",
"hh",
"hpp",
"hxx", # C++
"cu", # CUDA
# Other languages that clang-format supports
'proto',
'protodevel', # Protocol Buffers
'java', # Java
'js', # JavaScript
'ts', # TypeScript
'cs', # C Sharp
"proto",
"protodevel", # Protocol Buffers
"java", # Java
"js", # JavaScript
"ts", # TypeScript
"cs", # C Sharp
]
)
p = argparse.ArgumentParser(usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, description=desc)
p.add_argument('--binary', default=config.get('clangformat.binary', 'clang-format'), help='path to clang-format'),
p = argparse.ArgumentParser(
usage=usage,
formatter_class=argparse.RawDescriptionHelpFormatter,
description=desc,
)
p.add_argument(
'--commit', default=config.get('clangformat.commit', 'HEAD'), help='default commit to use if none is specified'
"--binary",
default=config.get("clangformat.binary", "clang-format"),
help="path to clang-format",
),
p.add_argument('--diff', action='store_true', help='print a diff instead of applying the changes')
p.add_argument(
'--extensions',
default=config.get('clangformat.extensions', default_extensions),
help=('comma-separated list of file extensions to format, ' 'excluding the period and case-insensitive'),
"--commit",
default=config.get("clangformat.commit", "HEAD"),
help="default commit to use if none is specified",
),
p.add_argument('-f', '--force', action='store_true', help='allow changes to unstaged files')
p.add_argument('-p', '--patch', action='store_true', help='select hunks interactively')
p.add_argument('-q', '--quiet', action='count', default=0, help='print less information')
p.add_argument('--style', default=config.get('clangformat.style', None), help='passed to clang-format'),
p.add_argument('-v', '--verbose', action='count', default=0, help='print extra information')
p.add_argument(
"--diff",
action="store_true",
help="print a diff instead of applying the changes",
)
p.add_argument(
"--extensions",
default=config.get("clangformat.extensions", default_extensions),
help=("comma-separated list of file extensions to format, " "excluding the period and case-insensitive"),
),
p.add_argument("-f", "--force", action="store_true", help="allow changes to unstaged files")
p.add_argument("-p", "--patch", action="store_true", help="select hunks interactively")
p.add_argument("-q", "--quiet", action="count", default=0, help="print less information")
p.add_argument(
"--style",
default=config.get("clangformat.style", None),
help="passed to clang-format",
),
p.add_argument("-v", "--verbose", action="count", default=0, help="print extra information")
# We gather all the remaining positional arguments into 'args' since we need
# to use some heuristics to determine whether or not <commit> was present.
# However, to print pretty messages, we make use of metavar and help.
p.add_argument('args', nargs='*', metavar='<commit>', help='revision from which to compute the diff')
p.add_argument(
'ignored', nargs='*', metavar='<file>...', help='if specified, only consider differences in these files'
"args",
nargs="*",
metavar="<commit>",
help="revision from which to compute the diff",
)
p.add_argument(
"ignored",
nargs="*",
metavar="<file>...",
help="if specified, only consider differences in these files",
)
opts = p.parse_args(argv)
@ -130,26 +156,26 @@ def main():
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
if len(commits) > 1:
if not opts.diff:
die('--diff is required when two commits are given')
die("--diff is required when two commits are given")
else:
if len(commits) > 2:
die('at most two commits allowed; %d given' % len(commits))
die("at most two commits allowed; %d given" % len(commits))
changed_lines = compute_diff_and_extract_lines(commits, files)
if opts.verbose >= 1:
ignored_files = set(changed_lines)
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
filter_by_extension(changed_lines, opts.extensions.lower().split(","))
if opts.verbose >= 1:
ignored_files.difference_update(changed_lines)
if ignored_files:
print('Ignoring changes in the following files (wrong extension):')
print("Ignoring changes in the following files (wrong extension):")
for filename in ignored_files:
print(' %s' % filename)
print(" %s" % filename)
if changed_lines:
print('Running clang-format on the following files:')
print("Running clang-format on the following files:")
for filename in changed_lines:
print(' %s' % filename)
print(" %s" % filename)
if not changed_lines:
print('no modified files to format')
print("no modified files to format")
return
# The computed diff outputs absolute paths, so we must cd before accessing
# those files.
@ -163,19 +189,19 @@ def main():
old_tree = create_tree_from_workdir(changed_lines)
new_tree = run_clang_format_and_save_to_tree(changed_lines, binary=opts.binary, style=opts.style)
if opts.verbose >= 1:
print('old tree: %s' % old_tree)
print('new tree: %s' % new_tree)
print("old tree: %s" % old_tree)
print("new tree: %s" % new_tree)
if old_tree == new_tree:
if opts.verbose >= 0:
print('clang-format did not modify any files')
print("clang-format did not modify any files")
elif opts.diff:
print_diff(old_tree, new_tree)
else:
changed_files = apply_changes(old_tree, new_tree, force=opts.force, patch_mode=opts.patch)
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
print('changed files:')
print("changed files:")
for filename in changed_files:
print(' %s' % filename)
print(" %s" % filename)
def load_git_config(non_string_options=None):
@ -187,11 +213,11 @@ def load_git_config(non_string_options=None):
if non_string_options is None:
non_string_options = {}
out = {}
for entry in run('git', 'config', '--list', '--null').split('\0'):
for entry in run("git", "config", "--list", "--null").split("\0"):
if entry:
name, value = entry.split('\n', 1)
name, value = entry.split("\n", 1)
if name in non_string_options:
value = run('git', 'config', non_string_options[name], name)
value = run("git", "config", non_string_options[name], name)
out[name] = value
return out
@ -213,7 +239,7 @@ def interpret_args(args, dash_dash, default_commit):
commits = args
for commit in commits:
object_type = get_object_type(commit)
if object_type not in ('commit', 'tag'):
if object_type not in ("commit", "tag"):
if object_type is None:
die("'%s' is not a commit" % commit)
else:
@ -238,19 +264,19 @@ def disambiguate_revision(value):
"""Returns True if `value` is a revision, False if it is a file, or dies."""
# If `value` is ambiguous (neither a commit nor a file), the following
# command will die with an appropriate error message.
run('git', 'rev-parse', value, verbose=False)
run("git", "rev-parse", value, verbose=False)
object_type = get_object_type(value)
if object_type is None:
return False
if object_type in ('commit', 'tag'):
if object_type in ("commit", "tag"):
return True
die('`%s` is a %s, but a commit or filename was expected' % (value, object_type))
die("`%s` is a %s, but a commit or filename was expected" % (value, object_type))
def get_object_type(value):
"""Returns a string description of an object's type, or None if it is not
a valid git object."""
cmd = ['git', 'cat-file', '-t', value]
cmd = ["git", "cat-file", "-t", value]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
@ -277,10 +303,10 @@ def compute_diff(commits, files):
differences between the working directory and the first commit if a single
one was specified, or the difference between both specified commits, filtered
on `files` (if non-empty). Zero context lines are used in the patch."""
git_tool = 'diff-index'
git_tool = "diff-index"
if len(commits) > 1:
git_tool = 'diff-tree'
cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--']
git_tool = "diff-tree"
cmd = ["git", git_tool, "-p", "-U0"] + commits + ["--"]
cmd.extend(files)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.close()
@ -299,10 +325,10 @@ def extract_lines(patch_file):
matches = {}
for line in patch_file:
line = convert_string(line)
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
match = re.search(r"^\+\+\+\ [^/]+/(.*)", line)
if match:
filename = match.group(1).rstrip('\r\n')
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
filename = match.group(1).rstrip("\r\n")
match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
@ -320,8 +346,8 @@ def filter_by_extension(dictionary, allowed_extensions):
excluding the period."""
allowed_extensions = frozenset(allowed_extensions)
for filename in list(dictionary.keys()):
base_ext = filename.rsplit('.', 1)
if len(base_ext) == 1 and '' in allowed_extensions:
base_ext = filename.rsplit(".", 1)
if len(base_ext) == 1 and "" in allowed_extensions:
continue
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
del dictionary[filename]
@ -329,7 +355,7 @@ def filter_by_extension(dictionary, allowed_extensions):
def cd_to_toplevel():
"""Change to the top level of the git repository."""
toplevel = run('git', 'rev-parse', '--show-toplevel')
toplevel = run("git", "rev-parse", "--show-toplevel")
os.chdir(toplevel)
@ -337,10 +363,10 @@ def create_tree_from_workdir(filenames):
"""Create a new git tree with the given files from the working directory.
Returns the object ID (SHA-1) of the created tree."""
return create_tree(filenames, '--stdin')
return create_tree(filenames, "--stdin")
def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary='clang-format', style=None):
def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary="clang-format", style=None):
"""Run clang-format on each file and save the result to a git tree.
Returns the object ID (SHA-1) of the created tree."""
@ -355,9 +381,9 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary='clan
for filename, line_ranges in iteritems(changed_lines):
if revision:
git_metadata_cmd = [
'git',
'ls-tree',
'%s:%s' % (revision, os.path.dirname(filename)),
"git",
"ls-tree",
"%s:%s" % (revision, os.path.dirname(filename)),
os.path.basename(filename),
]
git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
@ -366,12 +392,12 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None, binary='clan
else:
mode = oct(os.stat(filename).st_mode)
# Adjust python3 octal format so that it matches what git expects
if mode.startswith('0o'):
mode = '0' + mode[2:]
if mode.startswith("0o"):
mode = "0" + mode[2:]
blob_id = clang_format_to_blob(filename, line_ranges, revision=revision, binary=binary, style=style)
yield '%s %s\t%s' % (mode, blob_id, filename)
yield "%s %s\t%s" % (mode, blob_id, filename)
return create_tree(index_info_generator(), '--index-info')
return create_tree(index_info_generator(), "--index-info")
def create_tree(input_lines, mode):
@ -381,20 +407,20 @@ def create_tree(input_lines, mode):
'--index-info' is must be a list of values suitable for "git update-index
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode
is invalid."""
assert mode in ('--stdin', '--index-info')
cmd = ['git', 'update-index', '--add', '-z', mode]
assert mode in ("--stdin", "--index-info")
cmd = ["git", "update-index", "--add", "-z", mode]
with temporary_index_file():
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
for line in input_lines:
p.stdin.write(to_bytes('%s\0' % line))
p.stdin.write(to_bytes("%s\0" % line))
p.stdin.close()
if p.wait() != 0:
die('`%s` failed' % ' '.join(cmd))
tree_id = run('git', 'write-tree')
die("`%s` failed" % " ".join(cmd))
tree_id = run("git", "write-tree")
return tree_id
def clang_format_to_blob(filename, line_ranges, revision=None, binary='clang-format', style=None):
def clang_format_to_blob(filename, line_ranges, revision=None, binary="clang-format", style=None):
"""Run clang-format on the given file and save the result to a git blob.
Runs on the file in `revision` if not None, or on the file in the working
@ -403,13 +429,13 @@ def clang_format_to_blob(filename, line_ranges, revision=None, binary='clang-for
Returns the object ID (SHA-1) of the created blob."""
clang_format_cmd = [binary]
if style:
clang_format_cmd.extend(['-style=' + style])
clang_format_cmd.extend(["-style=" + style])
clang_format_cmd.extend(
['-lines=%s:%s' % (start_line, start_line + line_count - 1) for start_line, line_count in line_ranges]
["-lines=%s:%s" % (start_line, start_line + line_count - 1) for start_line, line_count in line_ranges]
)
if revision:
clang_format_cmd.extend(['-assume-filename=' + filename])
git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
clang_format_cmd.extend(["-assume-filename=" + filename])
git_show_cmd = ["git", "cat-file", "blob", "%s:%s" % (revision, filename)]
git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
git_show.stdin.close()
clang_format_stdin = git_show.stdout
@ -427,17 +453,17 @@ def clang_format_to_blob(filename, line_ranges, revision=None, binary='clang-for
else:
raise
clang_format_stdin.close()
hash_object_cmd = ['git', 'hash-object', '-w', '--path=' + filename, '--stdin']
hash_object_cmd = ["git", "hash-object", "-w", "--path=" + filename, "--stdin"]
hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE)
clang_format.stdout.close()
stdout = hash_object.communicate()[0]
if hash_object.returncode != 0:
die('`%s` failed' % ' '.join(hash_object_cmd))
die("`%s` failed" % " ".join(hash_object_cmd))
if clang_format.wait() != 0:
die('`%s` failed' % ' '.join(clang_format_cmd))
die("`%s` failed" % " ".join(clang_format_cmd))
if git_show and git_show.wait() != 0:
die('`%s` failed' % ' '.join(git_show_cmd))
return convert_string(stdout).rstrip('\r\n')
die("`%s` failed" % " ".join(git_show_cmd))
return convert_string(stdout).rstrip("\r\n")
@contextlib.contextmanager
@ -445,15 +471,15 @@ def temporary_index_file(tree=None):
"""Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
the file afterward."""
index_path = create_temporary_index(tree)
old_index_path = os.environ.get('GIT_INDEX_FILE')
os.environ['GIT_INDEX_FILE'] = index_path
old_index_path = os.environ.get("GIT_INDEX_FILE")
os.environ["GIT_INDEX_FILE"] = index_path
try:
yield
finally:
if old_index_path is None:
del os.environ['GIT_INDEX_FILE']
del os.environ["GIT_INDEX_FILE"]
else:
os.environ['GIT_INDEX_FILE'] = old_index_path
os.environ["GIT_INDEX_FILE"] = old_index_path
os.remove(index_path)
@ -462,11 +488,11 @@ def create_temporary_index(tree=None):
If `tree` is not None, use that as the tree to read in. Otherwise, an
empty index is created."""
gitdir = run('git', 'rev-parse', '--git-dir')
gitdir = run("git", "rev-parse", "--git-dir")
path = os.path.join(gitdir, temp_index_basename)
if tree is None:
tree = '--empty'
run('git', 'read-tree', '--index-output=' + path, tree)
tree = "--empty"
run("git", "read-tree", "--index-output=" + path, tree)
return path
@ -479,7 +505,7 @@ def print_diff(old_tree, new_tree):
# We also only print modified files since `new_tree` only contains the files
# that were modified, so unmodified files would show as deleted without the
# filter.
subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree, '--'])
subprocess.check_call(["git", "diff", "--diff-filter=M", old_tree, new_tree, "--"])
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
@ -488,16 +514,28 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
Bails if there are local changes in those files and not `force`. If
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
changed_files = (
run('git', 'diff-tree', '--diff-filter=M', '-r', '-z', '--name-only', old_tree, new_tree)
.rstrip('\0')
.split('\0')
run(
"git",
"diff-tree",
"--diff-filter=M",
"-r",
"-z",
"--name-only",
old_tree,
new_tree,
)
.rstrip("\0")
.split("\0")
)
if not force:
unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
unstaged_files = run("git", "diff-files", "--name-status", *changed_files)
if unstaged_files:
print('The following files would be modified but ' 'have unstaged changes:', file=sys.stderr)
print(
"The following files would be modified but " "have unstaged changes:",
file=sys.stderr,
)
print(unstaged_files, file=sys.stderr)
print('Please commit, stage, or stash them first.', file=sys.stderr)
print("Please commit, stage, or stash them first.", file=sys.stderr)
sys.exit(2)
if patch_mode:
# In patch mode, we could just as well create an index from the new tree
@ -507,17 +545,17 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
# better message, "Apply ... to index and worktree". This is not quite
# right, since it won't be applied to the user's index, but oh well.
with temporary_index_file(old_tree):
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
subprocess.check_call(["git", "checkout", "--patch", new_tree])
else:
with temporary_index_file(new_tree):
run('git', 'checkout-index', '-a', '-f')
run("git", "checkout-index", "-a", "-f")
return changed_files
def run(*args, **kwargs):
stdin = kwargs.pop('stdin', '')
verbose = kwargs.pop('verbose', True)
strip = kwargs.pop('strip', True)
stdin = kwargs.pop("stdin", "")
verbose = kwargs.pop("verbose", True)
strip = kwargs.pop("strip", True)
for name in kwargs:
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
@ -529,20 +567,20 @@ def run(*args, **kwargs):
if p.returncode == 0:
if stderr:
if verbose:
print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr)
print("`%s` printed to stderr:" % " ".join(args), file=sys.stderr)
print(stderr.rstrip(), file=sys.stderr)
if strip:
stdout = stdout.rstrip('\r\n')
stdout = stdout.rstrip("\r\n")
return stdout
if verbose:
print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr)
print("`%s` returned %s" % (" ".join(args), p.returncode), file=sys.stderr)
if stderr:
print(stderr.rstrip(), file=sys.stderr)
sys.exit(2)
def die(message):
print('error:', message, file=sys.stderr)
print("error:", message, file=sys.stderr)
sys.exit(2)
@ -550,23 +588,23 @@ def to_bytes(str_input):
# Encode to UTF-8 to get binary data.
if isinstance(str_input, bytes):
return str_input
return str_input.encode('utf-8')
return str_input.encode("utf-8")
def to_string(bytes_input):
if isinstance(bytes_input, str):
return bytes_input
return bytes_input.encode('utf-8')
return bytes_input.encode("utf-8")
def convert_string(bytes_input):
try:
return to_string(bytes_input.decode('utf-8'))
return to_string(bytes_input.decode("utf-8"))
except AttributeError: # 'str' object has no attribute 'decode'.
return str(bytes_input)
except UnicodeError:
return str(bytes_input)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -38,6 +38,7 @@ public:
boost::asio::awaitable<void> vitalentum(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> gptGo(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> aibn(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> chatGptDuo(std::shared_ptr<Channel>, nlohmann::json);
private:
boost::asio::awaitable<std::expected<boost::beast::ssl_stream<boost::beast::tcp_stream>, std::string>>

View File

@ -517,7 +517,7 @@ std::optional<HttpResponse> getCookie(CURL* curl, const std::string& url, const
} // namespace
FreeGpt::FreeGpt(Config& cfg)
: m_cfg(cfg), m_thread_pool_ptr(std::make_shared<boost::asio::thread_pool>(m_cfg.work_thread_num)) {}
: m_cfg(cfg), m_thread_pool_ptr(std::make_shared<boost::asio::thread_pool>(m_cfg.work_thread_num * 2)) {}
boost::asio::awaitable<std::expected<boost::beast::ssl_stream<boost::beast::tcp_stream>, std::string>>
FreeGpt::createHttpClient(boost::asio::ssl::context& ctx, std::string_view host, std::string_view port) {
@ -2116,8 +2116,6 @@ boost::asio::awaitable<void> FreeGpt::ylokh(std::shared_ptr<Channel> ch, nlohman
boost::system::error_code err{};
ScopeExit auto_exit{[&] { ch->close(); }};
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
constexpr std::string_view host = "chatapi.ylokh.xyz";
constexpr std::string_view port = "443";
@ -2153,10 +2151,6 @@ boost::asio::awaitable<void> FreeGpt::ylokh(std::shared_ptr<Channel> ch, nlohman
{
"role":"system",
"content":"Carefully heed the user's instructions and follow the user's will to the best of your ability.\nRespond using Markdown."
},
{
"role":"user",
"content":"hello"
}
],
"model":"gpt-3.5-turbo-16k",
@ -2169,7 +2163,10 @@ boost::asio::awaitable<void> FreeGpt::ylokh(std::shared_ptr<Channel> ch, nlohman
})";
nlohmann::json request = nlohmann::json::parse(json_str, nullptr, false);
request["messages"][1]["content"] = prompt;
auto conversation = getConversationJson(json);
for (const auto& item : conversation)
request["messages"].push_back(item);
SPDLOG_INFO("{}", request.dump(2));
req.body() = request.dump();
@ -2595,7 +2592,7 @@ boost::asio::awaitable<void> FreeGpt::aibn(std::shared_ptr<Channel> ch, nlohmann
request["sign"] = signature;
request["time"] = timestamp;
request["messages"][0]["content"] = prompt;
request["messages"] = getConversationJson(json);
auto str = request.dump();
SPDLOG_INFO("request : [{}]", str);
@ -2628,3 +2625,81 @@ boost::asio::awaitable<void> FreeGpt::aibn(std::shared_ptr<Channel> ch, nlohmann
}
co_return;
}
boost::asio::awaitable<void> FreeGpt::chatGptDuo(std::shared_ptr<Channel> ch, nlohmann::json json) {
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
boost::system::error_code err{};
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
CURLcode res;
CURL* curl = curl_easy_init();
if (!curl) {
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, error_info);
co_return;
}
curl_easy_setopt(curl, CURLOPT_URL, "https://chatgptduo.com/");
auto request_data = urlEncode(std::format("prompt=('{}',)&search=('{}',)&purpose=ask", prompt, prompt));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request_data.c_str());
struct Input {
std::shared_ptr<Channel> ch;
};
Input input{ch};
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
boost::system::error_code err{};
auto input_ptr = static_cast<Input*>(userp);
std::string data{(char*)contents, size * nmemb};
auto& [ch] = *input_ptr;
boost::asio::post(ch->get_executor(), [=, data = std::move(data)] mutable {
nlohmann::json json = nlohmann::json::parse(data, nullptr, false);
if (json.is_discarded()) {
SPDLOG_ERROR("json parse error: [{}]", data);
ch->try_send(err, data);
return;
}
if (json.contains("answer")) {
auto str = json["answer"].get<std::string>();
ch->try_send(err, str);
} else {
ch->try_send(err, std::format("Invalid JSON: {}", json.dump()));
}
return;
});
return size * nmemb;
};
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
curlEasySetopt(curl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
ScopeExit auto_exit{[=] {
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}};
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
ch->try_send(err, error_info);
co_return;
}
int32_t response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, std::format("you http code:{}", response_code));
co_return;
}
co_return;
}

View File

@ -2,6 +2,7 @@
#include <format>
#include <functional>
#include <regex>
#include <semaphore>
#include <string>
@ -38,8 +39,11 @@ void setEnvironment(auto& cfg) {
if (!upper_http_proxy.empty())
cfg.http_proxy = std::move(upper_http_proxy);
}
if (auto [chat_path] = getEnv("CHAT_PATH"); !chat_path.empty())
if (auto [chat_path] = getEnv("CHAT_PATH"); !chat_path.empty()) {
cfg.chat_path = std::move(chat_path);
}
if (cfg.chat_path.back() == '/')
cfg.chat_path.pop_back();
if (auto [port] = getEnv("PORT"); !port.empty())
cfg.port = std::move(port);
if (auto [host] = getEnv("HOST"); !host.empty())
@ -67,6 +71,7 @@ std::string createIndexHtml(const std::string& file, const Config& cfg) {
inja::Environment env;
nlohmann::json data;
data["chat_id"] = createUuidString();
data["chat_path"] = cfg.chat_path;
if (!cfg.providers.empty()) {
data["model_list"] = cfg.providers;
} else {
@ -113,6 +118,8 @@ boost::asio::awaitable<void> startSession(boost::asio::ip::tcp::socket sock, Con
co_await boost::beast::async_write(stream, std::move(rsp), use_nothrow_awaitable);
co_return;
}
auto assets_path = std::format("{}{}", cfg.chat_path, ASSETS_PATH);
SPDLOG_INFO("assets_path: [{}]", assets_path);
while (true) {
boost::beast::flat_buffer buffer;
boost::beast::http::request<boost::beast::http::string_body> request;
@ -142,15 +149,25 @@ boost::asio::awaitable<void> startSession(boost::asio::ip::tcp::socket sock, Con
res.prepare_payload();
boost::beast::http::message_generator rsp = std::move(res);
co_await boost::beast::async_write(stream, std::move(rsp), use_nothrow_awaitable);
} else if (request.target().starts_with(ASSETS_PATH)) {
} else if (request.target().starts_with(assets_path)) {
std::string req_path{request.target()};
req_path.erase(req_path.find(ASSETS_PATH), ASSETS_PATH.length());
SPDLOG_INFO("req_path: {}", req_path);
req_path.erase(req_path.find(assets_path), assets_path.length());
auto file = std::format("{}{}", cfg.client_root_path, req_path);
SPDLOG_INFO("load: {}", file);
if (file.contains("chat.js")) {
if (file.contains("chat.js") || file.contains("site.webmanifest")) {
inja::Environment env;
nlohmann::json data;
data["chat_path"] = cfg.chat_path;
if (file.contains("chat.js")) {
auto format_string = [](const std::string& str) {
std::regex pattern("/");
std::string replacement = "\\/";
return std::regex_replace(str, pattern, replacement);
};
data["chat_path"] = format_string(cfg.chat_path);
} else {
data["chat_path"] = cfg.chat_path;
}
auto chat_js_content = env.render_file(file, data);
boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::ok,
request.version()};
@ -307,7 +324,7 @@ int main(int argc, char** argv) {
ADD_METHOD("gpt-3.5-turbo-stream-openai", FreeGpt::openAi);
ADD_METHOD("gpt-3.5-turbo-Aichat", FreeGpt::aiChat);
ADD_METHOD("gpt-4-ChatgptAi", FreeGpt::chatGptAi);
ADD_METHOD("gpt-3.5-turbo-weWordle", FreeGpt::weWordle);
// ADD_METHOD("gpt-3.5-turbo-weWordle", FreeGpt::weWordle);
ADD_METHOD("gpt-3.5-turbo-acytoo", FreeGpt::acytoo);
ADD_METHOD("gpt-3.5-turbo-stream-DeepAi", FreeGpt::deepAi);
ADD_METHOD("gpt-3.5-turbo-stream-H2o", FreeGpt::h2o);
@ -316,7 +333,7 @@ int main(int argc, char** argv) {
ADD_METHOD("gpt-4-turbo-stream-you", FreeGpt::you);
ADD_METHOD("gpt-3.5-turbo-AItianhu", FreeGpt::aiTianhu);
ADD_METHOD("gpt-3-stream-binjie", FreeGpt::binjie);
ADD_METHOD("gpt-3.5-turbo-stream-CodeLinkAva", FreeGpt::codeLinkAva);
// ADD_METHOD("gpt-3.5-turbo-stream-CodeLinkAva", FreeGpt::codeLinkAva);
ADD_METHOD("gpt-4-stream-ChatBase", FreeGpt::chatBase);
ADD_METHOD("gpt-3.5-turbo-stream-aivvm", FreeGpt::aivvm);
ADD_METHOD("gpt-3.5-turbo-16k-stream-Ylokh", FreeGpt::ylokh);
@ -324,6 +341,7 @@ int main(int argc, char** argv) {
ADD_METHOD("gpt-3.5-turbo-stream-GptGo", FreeGpt::gptGo);
ADD_METHOD("gpt-3.5-turbo-stream-AItianhuSpace", FreeGpt::aiTianhuSpace);
ADD_METHOD("gpt-3.5-turbo-stream-Aibn", FreeGpt::aibn);
ADD_METHOD("gpt-3.5-turbo-ChatgptDuo", FreeGpt::chatGptDuo);
IoContextPool pool{cfg.work_thread_num};
pool.start();

View File

@ -33,7 +33,7 @@ services:
command: bash start-bot.sh
chat-gpt:
image: "fantasypeak/freegpt:latest"
image: "balshdocker/freegpt:latest"
container_name: "chat_gpt_chat_service"
hostname: "chat_service"
restart: unless-stopped

2064
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ python-telegram-bot = {version = "^20.5", extras=["ext"]}
python-dotenv = "^1.0"
httpx = "^0.24"
loguru = "^0.7"
pydantic = "^2.3"
pydantic = "^2.4"
pydantic-settings = "^2.0.3"
gunicorn = "^21.2"
uvicorn = "^0.23"
@ -21,33 +21,7 @@ orjson = "^3.9"
sentry-sdk = "^1.31.0"
SpeechRecognition = "^3.8"
pydub = "^0.25"
websocket-client = "^1.6"
greenlet = "^2.0.2"
requests = "^2.31"
selenium = "^4.11"
tls-client = "^0.2"
pypasser = "^0.0.5"
names = "^0.3"
colorama = "^0.4"
curl_cffi = "0.5.7"
aiohttp = "^3.8.5"
flask = "^2.3"
flask_cors = "^4.0"
flask-babel = "^3.1"
streamlit = "^1.26"
fake-useragent = "^1.2"
twocaptcha = "^0.0.1"
pymailtm = "^1.1"
Levenshtein = "^0.21"
retrying = "^1.3"
mailgw_temporary_email = "^0.0.2"
pycryptodome = "^3.18"
random-password-generator = "^2.2"
numpy = "^1.25"
tornado = "^6.3"
PyExecJS = "^1.5"
browser_cookie3 = "^0.19"
[tool.poetry.dev-dependencies]