mirror of
https://github.com/yihong0618/bilingual_book_maker.git
synced 2025-07-19 16:40:08 +00:00
Merge branch 'main' of https://github.com/umm233/bilingual_book_maker
This commit is contained in:
commit
147819eaf4
2
.github/workflows/make_test_ebook.yaml
vendored
2
.github/workflows/make_test_ebook.yaml
vendored
@ -71,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Rename and Upload ePub
|
||||
if: env.OPENAI_API_KEY != null
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: epub_output
|
||||
path: "test_books/lemo_bilingual.epub"
|
||||
|
@ -27,6 +27,9 @@ bilingual_book_maker 是一个 AI 翻译工具,使用 ChatGPT 帮助用户制
|
||||
- 可以使用彩云进行翻译 `--model caiyun --caiyun_key ${caiyun_key}`
|
||||
- 可以使用 Gemini 进行翻译 `--model gemini --gemini_key ${gemini_key}`
|
||||
- 可以使用腾讯交互翻译(免费)进行翻译`--model tencentransmart`
|
||||
- 可以使用[xAI](https://x.ai)进行翻译`--model xai --xai_key ${xai_key}`
|
||||
- 可以使用 [Ollama](https://github.com/ollama/ollama) 自托管模型进行翻译,使用 `--ollama_model ${ollama_model_name}`
|
||||
- 如果 ollama server 不运行在本地,使用 `--api_base http://x.x.x.x:port/v1` 指向 ollama server 地址
|
||||
- 使用 `--test` 命令如果大家没付费可以加上这个先看看效果(有 limit 稍微有些慢)
|
||||
- 使用 `--language` 指定目标语言,例如: `--language "Simplified Chinese"`,预设值为 `"Simplified Chinese"`.
|
||||
请阅读 helper message 来查找可用的目标语言: `python make_book.py --help`
|
||||
|
24
README.md
24
README.md
@ -24,14 +24,19 @@ Find more info here for using liteLLM: https://github.com/BerriAI/litellm/blob/m
|
||||
- Use `--openai_key` option to specify OpenAI API key. If you have multiple keys, separate them by commas (xxx,xxx,xxx) to reduce errors caused by API call limits.
|
||||
Or, just set environment variable `BBM_OPENAI_API_KEY` instead.
|
||||
- A sample book, `test_books/animal_farm.epub`, is provided for testing purposes.
|
||||
- The default underlying model is [GPT-3.5-turbo](https://openai.com/blog/introducing-chatgpt-and-whisper-apis), which is used by ChatGPT currently. Use `--model gpt4` to change the underlying model to `GPT4`.
|
||||
- The default underlying model is [GPT-3.5-turbo](https://openai.com/blog/introducing-chatgpt-and-whisper-apis), which is used by ChatGPT currently. Use `--model gpt4` to change the underlying model to `GPT4`. You can also use `GPT4omini`.
|
||||
- Important to note that `gpt-4` is significantly more expensive than `gpt-4-turbo`, but to avoid bumping into rate limits, we automatically balance queries across `gpt-4-1106-preview`, `gpt-4`, `gpt-4-32k`, `gpt-4-0613`,`gpt-4-32k-0613`.
|
||||
- If you want to use a specific model alias with OpenAI (eg `gpt-4-1106-preview` or `gpt-3.5-turbo-0125`), you can use `--model openai --model_list gpt-4-1106-preview,gpt-3.5-turbo-0125`. `--model_list` takes a comma-separated list of model aliases.
|
||||
- If using `GPT4`, you can add `--use_context` to add a context paragraph to each passage sent to the model for translation (see below).- support DeepL model [DeepL Translator](https://rapidapi.com/splintPRO/api/dpl-translator) need pay to get the token use `--model deepl --deepl_key ${deepl_key}`
|
||||
- support DeepL free model `--model deeplfree`
|
||||
- support Google [Gemini](https://makersuite.google.com/app/apikey) model `--model gemini --gemini_key ${gemini_key}`
|
||||
- If using chatgptapi, you can add `--use_context` to add a context paragraph to each passage sent to the model for translation (see below).
|
||||
- Support DeepL model [DeepL Translator](https://rapidapi.com/splintPRO/api/dpl-translator) need pay to get the token use `--model deepl --deepl_key ${deepl_key}`
|
||||
- Support DeepL free model `--model deeplfree`
|
||||
- Support Google [Gemini](https://aistudio.google.com/app/apikey) model, use `--model gemini` for Gemini Flash or `--model geminipro` for Gemini Pro. `--gemini_key ${gemini_key}`
|
||||
- If you want to use a specific model alias with Gemini (eg `gemini-1.5-flash-002` or `gemini-1.5-flash-8b-exp-0924`), you can use `--model gemini --model_list gemini-1.5-flash-002,gemini-1.5-flash-8b-exp-0924`. `--model_list` takes a comma-separated list of model aliases.
|
||||
- Support [Claude](https://console.anthropic.com/docs) model, use `--model claude --claude_key ${claude_key}`
|
||||
- Support [Tencent TranSmart](https://transmart.qq.com) model (Free), use `--model tencentransmart`
|
||||
- Support [xAI](https://x.ai) model, use `--model xai --xai_key ${xai_key}`
|
||||
- Support [Ollama](https://github.com/ollama/ollama) self-host models, use `--ollama_model ${ollama_model_name}`
|
||||
- If ollama server is not running on localhost, use `--api_base http://x.x.x.x:port/v1` to point to the ollama server address
|
||||
- Use `--test` option to preview the result if you haven't paid for the service. Note that there is a limit and it may take some time.
|
||||
- Set the target language like `--language "Simplified Chinese"`. Default target language is `"Simplified Chinese"`.
|
||||
Read available languages by helper message: `python make_book.py --help`
|
||||
@ -54,7 +59,11 @@ Find more info here for using liteLLM: https://github.com/BerriAI/litellm/blob/m
|
||||
- `--accumulated_num` Wait for how many tokens have been accumulated before starting the translation. gpt3.5 limits the total_token to 4090. For example, if you use --accumulated_num 1600, maybe openai will
|
||||
output 2200 tokens and maybe 200 tokens for other messages in the system messages user messages, 1600+2200+200=4000, So you are close to reaching the limit. You have to choose your own
|
||||
value, there is no way to know if the limit is reached before sending
|
||||
- `--use_context` prompts the GPT4 model to create a one-paragraph summary. If it's the beginning of the translation, it will summarize the entire passage sent (the size depending on `--accumulated_num`), but if it's any proceeding passage, it will amend the summary to include details from the most recent passage, creating a running one-paragraph context payload of the important details of the entire translated work, which improves consistency of flow and tone of each translation.
|
||||
- `--use_context` prompts the model to create a three-paragraph summary. If it's the beginning of the translation, it will summarize the entire passage sent (the size depending on `--accumulated_num`). For subsequent passages, it will amend the summary to include details from the most recent passage, creating a running one-paragraph context payload of the important details of the entire translated work. This improves consistency of flow and tone throughout the translation. This option is available for all ChatGPT-compatible models and Gemini models.
|
||||
- Use `--context_paragraph_limit` to set a limit on the number of context paragraphs when using the `--use_context` option.
|
||||
- Use `--temperature` to set the temperature parameter for `chatgptapi`/`gpt4`/`claude` models. For example: `--temperature 0.7`.
|
||||
- Use `--block_size` to merge multiple paragraphs into one block. This may increase accuracy and speed up the process but can disturb the original format. Must be used with `--single_translate`. For example: `--block_size 5`.
|
||||
- Use `--single_translate` to output only the translated book without creating a bilingual version.
|
||||
- `--translation_style` example: `--translation_style "color: #808080; font-style: italic;"`
|
||||
- `--retranslate` `--retranslate "$translated_filepath" "file_name_in_epub" "start_str" "end_str"(optional)`<br>
|
||||
Retranslate from start_str to end_str's tag:
|
||||
@ -75,9 +84,12 @@ python3 make_book.py --book_name test_books/Lex_Fridman_episode_322.srt --openai
|
||||
# Or translate the whole book
|
||||
python3 make_book.py --book_name test_books/animal_farm.epub --openai_key ${openai_key} --language zh-hans
|
||||
|
||||
# Or translate the whole book using Gemini
|
||||
# Or translate the whole book using Gemini flash
|
||||
python3 make_book.py --book_name test_books/animal_farm.epub --gemini_key ${gemini_key} --model gemini
|
||||
|
||||
# Use a specific list of Gemini model aliases
|
||||
python3 make_book.py --book_name test_books/animal_farm.epub --gemini_key ${gemini_key} --model gemini --model_list gemini-1.5-flash-002,gemini-1.5-flash-8b-exp-0924
|
||||
|
||||
# Set env OPENAI_API_KEY to ignore option --openai_key
|
||||
export OPENAI_API_KEY=${your_api_key}
|
||||
|
||||
|
@ -122,6 +122,14 @@ def main():
|
||||
help="You can get Groq Key from https://console.groq.com/keys",
|
||||
)
|
||||
|
||||
# for xAI
|
||||
parser.add_argument(
|
||||
"--xai_key",
|
||||
dest="xai_key",
|
||||
type=str,
|
||||
help="You can get xAI Key from https://console.x.ai/",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--test",
|
||||
dest="test",
|
||||
@ -149,7 +157,7 @@ def main():
|
||||
"--ollama_model",
|
||||
dest="ollama_model",
|
||||
type=str,
|
||||
default="ollama_model",
|
||||
default="",
|
||||
metavar="MODEL",
|
||||
help="use ollama",
|
||||
)
|
||||
@ -279,11 +287,18 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
action="store_true",
|
||||
help="adds an additional paragraph for global, updating historical context of the story to the model's input, improving the narrative consistency for the AI model (this uses ~200 more tokens each time)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--context_paragraph_limit",
|
||||
dest="context_paragraph_limit",
|
||||
type=int,
|
||||
default=0,
|
||||
help="if use --use_context, set context paragraph limit",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--temperature",
|
||||
type=float,
|
||||
default=1.0,
|
||||
help="temperature parameter for `chatgptapi`/`gpt4`/`claude`",
|
||||
help="temperature parameter for `chatgptapi`/`gpt4`/`claude`/`gemini`",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--block_size",
|
||||
@ -297,11 +312,32 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
dest="model_list",
|
||||
help="Rather than using our preset lists of models, specify exactly the models you want as a comma separated list `gpt-4-32k,gpt-3.5-turbo-0125` (Currently only supports: `openai`)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--batch",
|
||||
dest="batch_flag",
|
||||
action="store_true",
|
||||
help="Enable batch translation using ChatGPT's batch API for improved efficiency",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--batch-use",
|
||||
dest="batch_use_flag",
|
||||
action="store_true",
|
||||
help="Use pre-generated batch translations to create files. Run with --batch first before using this option",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--interval",
|
||||
type=float,
|
||||
default=0.01,
|
||||
help="Request interval in seconds (e.g., 0.1 for 100ms). Currently only supported for Gemini models. Default: 0.01",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if not options.book_name:
|
||||
print(f"Error: please provide the path of your book using --book_name <path>")
|
||||
exit(1)
|
||||
if not os.path.isfile(options.book_name):
|
||||
print(f"Error: {options.book_name} does not exist.")
|
||||
print(f"Error: the book {options.book_name!r} does not exist.")
|
||||
exit(1)
|
||||
|
||||
PROXY = options.proxy
|
||||
@ -312,7 +348,7 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
translate_model = MODEL_DICT.get(options.model)
|
||||
assert translate_model is not None, "unsupported model"
|
||||
API_KEY = ""
|
||||
if options.model in ["openai", "chatgptapi", "gpt4"]:
|
||||
if options.model in ["openai", "chatgptapi", "gpt4", "gpt4omini", "gpt4o"]:
|
||||
if OPENAI_API_KEY := (
|
||||
options.openai_key
|
||||
or env.get(
|
||||
@ -347,10 +383,12 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
API_KEY = options.custom_api or env.get("BBM_CUSTOM_API")
|
||||
if not API_KEY:
|
||||
raise Exception("Please provide custom translate api")
|
||||
elif options.model == "gemini":
|
||||
elif options.model in ["gemini", "geminipro"]:
|
||||
API_KEY = options.gemini_key or env.get("BBM_GOOGLE_GEMINI_KEY")
|
||||
elif options.model == "groq":
|
||||
API_KEY = options.groq_key or env.get("BBM_GROQ_API_KEY")
|
||||
elif options.model == "xai":
|
||||
API_KEY = options.xai_key or env.get("BBM_XAI_API_KEY")
|
||||
else:
|
||||
API_KEY = ""
|
||||
|
||||
@ -429,6 +467,8 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
assert options.model in [
|
||||
"chatgptapi",
|
||||
"gpt4",
|
||||
"gpt4omini",
|
||||
"gpt4o",
|
||||
], "only support chatgptapi for deployment_id"
|
||||
if not options.api_base:
|
||||
raise ValueError("`api_base` must be provided when using `deployment_id`")
|
||||
@ -439,7 +479,7 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
e.translate_model.set_model_list(options.model_list.split(","))
|
||||
else:
|
||||
raise ValueError(
|
||||
"When using `openai` model, you must also provide `--model_list`. For default model sets use `--model chatgptapi` or `--model gpt4`",
|
||||
"When using `openai` model, you must also provide `--model_list`. For default model sets use `--model chatgptapi` or `--model gpt4` or `--model gpt4omini`",
|
||||
)
|
||||
# TODO refactor, quick fix for gpt4 model
|
||||
if options.model == "chatgptapi":
|
||||
@ -449,8 +489,26 @@ So you are close to reaching the limit. You have to choose your own value, there
|
||||
e.translate_model.set_gpt35_models()
|
||||
if options.model == "gpt4":
|
||||
e.translate_model.set_gpt4_models()
|
||||
if options.model == "gpt4omini":
|
||||
e.translate_model.set_gpt4omini_models()
|
||||
if options.model == "gpt4o":
|
||||
e.translate_model.set_gpt4o_models()
|
||||
if options.block_size > 0:
|
||||
e.block_size = options.block_size
|
||||
if options.batch_flag:
|
||||
e.batch_flag = options.batch_flag
|
||||
if options.batch_use_flag:
|
||||
e.batch_use_flag = options.batch_use_flag
|
||||
|
||||
if options.model in ("gemini", "geminipro"):
|
||||
e.translate_model.set_interval(options.interval)
|
||||
if options.model == "gemini":
|
||||
if options.model_list:
|
||||
e.translate_model.set_model_list(options.model_list.split(","))
|
||||
else:
|
||||
e.translate_model.set_geminiflash_models()
|
||||
if options.model == "geminipro":
|
||||
e.translate_model.set_geminipro_models()
|
||||
|
||||
e.make_bilingual_book()
|
||||
|
||||
|
8
book_maker/config.py
Normal file
8
book_maker/config.py
Normal file
@ -0,0 +1,8 @@
|
||||
config = {
|
||||
"translator": {
|
||||
"chatgptapi": {
|
||||
"context_paragraph_limit": 3,
|
||||
"batch_context_update_interval": 50,
|
||||
}
|
||||
},
|
||||
}
|
@ -2,6 +2,7 @@ import os
|
||||
import pickle
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
from copy import copy
|
||||
from pathlib import Path
|
||||
|
||||
@ -33,6 +34,7 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
single_translate=False,
|
||||
context_flag=False,
|
||||
temperature=1.0,
|
||||
context_paragraph_limit=0,
|
||||
):
|
||||
self.epub_name = epub_name
|
||||
self.new_epub = epub.EpubBook()
|
||||
@ -41,6 +43,7 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
language,
|
||||
api_base=model_api_base,
|
||||
context_flag=context_flag,
|
||||
context_paragraph_limit=context_paragraph_limit,
|
||||
temperature=temperature,
|
||||
**prompt_config_to_kwargs(prompt_config),
|
||||
)
|
||||
@ -63,6 +66,8 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
self.only_filelist = ""
|
||||
self.single_translate = single_translate
|
||||
self.block_size = -1
|
||||
self.batch_use_flag = False
|
||||
self.batch_flag = False
|
||||
|
||||
# monkey patch for # 173
|
||||
def _write_items_patch(obj):
|
||||
@ -140,11 +145,18 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
if self.resume and index < p_to_save_len:
|
||||
p.string = self.p_to_save[index]
|
||||
else:
|
||||
t_text = ""
|
||||
if self.batch_flag:
|
||||
self.translate_model.add_to_batch_translate_queue(index, new_p.text)
|
||||
elif self.batch_use_flag:
|
||||
t_text = self.translate_model.batch_translate(index)
|
||||
else:
|
||||
t_text = self.translate_model.translate(new_p.text)
|
||||
if type(p) == NavigableString:
|
||||
new_p = self.translate_model.translate(new_p.text)
|
||||
new_p = t_text
|
||||
self.p_to_save.append(new_p)
|
||||
else:
|
||||
new_p.string = self.translate_model.translate(new_p.text)
|
||||
new_p.string = t_text
|
||||
self.p_to_save.append(new_p.text)
|
||||
|
||||
self.helper.insert_trans(
|
||||
@ -454,6 +466,18 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
|
||||
return index
|
||||
|
||||
def batch_init_then_wait(self):
|
||||
name, _ = os.path.splitext(self.epub_name)
|
||||
if self.batch_flag or self.batch_use_flag:
|
||||
self.translate_model.batch_init(name)
|
||||
if self.batch_use_flag:
|
||||
start_time = time.time()
|
||||
while not self.translate_model.is_completed_batch():
|
||||
print("Batch translation is not completed yet")
|
||||
time.sleep(2)
|
||||
if time.time() - start_time > 300: # 5 minutes
|
||||
raise Exception("Batch translation timed out after 5 minutes")
|
||||
|
||||
def make_bilingual_book(self):
|
||||
self.helper = EPUBBookLoaderHelper(
|
||||
self.translate_model,
|
||||
@ -461,6 +485,7 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
self.translation_style,
|
||||
self.context_flag,
|
||||
)
|
||||
self.batch_init_then_wait()
|
||||
new_book = self._make_new_book(self.origin_book)
|
||||
all_items = list(self.origin_book.get_items())
|
||||
trans_taglist = self.translate_tags.split(",")
|
||||
@ -518,7 +543,10 @@ class EPUBBookLoader(BaseBookLoader):
|
||||
name, _ = os.path.splitext(self.epub_name)
|
||||
epub.write_epub(f"{name}_bilingual.epub", new_book, {})
|
||||
name, _ = os.path.splitext(self.epub_name)
|
||||
epub.write_epub(f"{name}_bilingual.epub", new_book, {})
|
||||
if self.batch_flag:
|
||||
self.translate_model.batch()
|
||||
else:
|
||||
epub.write_epub(f"{name}_bilingual.epub", new_book, {})
|
||||
if self.accumulated_num == 1:
|
||||
pbar.close()
|
||||
except (KeyboardInterrupt, Exception) as e:
|
||||
|
@ -1,7 +1,7 @@
|
||||
import re
|
||||
from copy import copy
|
||||
import backoff
|
||||
import logging
|
||||
from copy import copy
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -37,9 +37,10 @@ class EPUBBookLoaderHelper:
|
||||
Exception,
|
||||
on_backoff=lambda details: logger.warning(f"retry backoff: {details}"),
|
||||
on_giveup=lambda details: logger.warning(f"retry abort: {details}"),
|
||||
jitter=None,
|
||||
)
|
||||
def translate_with_backoff(self, **kwargs):
|
||||
return self.translate_model.translate(**kwargs)
|
||||
def translate_with_backoff(self, text, context_flag=False):
|
||||
return self.translate_model.translate(text, context_flag)
|
||||
|
||||
def deal_new(self, p, wait_p_list, single_translate=False):
|
||||
self.deal_old(wait_p_list, single_translate, self.context_flag)
|
||||
|
@ -8,19 +8,24 @@ from book_maker.translator.gemini_translator import Gemini
|
||||
from book_maker.translator.groq_translator import GroqClient
|
||||
from book_maker.translator.tencent_transmart_translator import TencentTranSmart
|
||||
from book_maker.translator.custom_api_translator import CustomAPI
|
||||
from book_maker.translator.xai_translator import XAIClient
|
||||
|
||||
MODEL_DICT = {
|
||||
"openai": ChatGPTAPI,
|
||||
"chatgptapi": ChatGPTAPI,
|
||||
"gpt4": ChatGPTAPI,
|
||||
"gpt4omini": ChatGPTAPI,
|
||||
"gpt4o": ChatGPTAPI,
|
||||
"google": Google,
|
||||
"caiyun": Caiyun,
|
||||
"deepl": DeepL,
|
||||
"deeplfree": DeepLFree,
|
||||
"claude": Claude,
|
||||
"gemini": Gemini,
|
||||
"geminipro": Gemini,
|
||||
"groq": GroqClient,
|
||||
"tencentransmart": TencentTranSmart,
|
||||
"customapi": CustomAPI,
|
||||
"xai": XAIClient,
|
||||
# add more here
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
import shutil
|
||||
from copy import copy
|
||||
from os import environ
|
||||
from itertools import cycle
|
||||
import json
|
||||
|
||||
from openai import AzureOpenAI, OpenAI, RateLimitError
|
||||
from rich import print
|
||||
|
||||
from .base_translator import Base
|
||||
from ..config import config
|
||||
|
||||
CHATGPT_CONFIG = config["translator"]["chatgptapi"]
|
||||
|
||||
PROMPT_ENV_MAP = {
|
||||
"user": "BBM_CHATGPTAPI_USER_MSG_TEMPLATE",
|
||||
@ -27,10 +33,22 @@ GPT4_MODEL_LIST = [
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4",
|
||||
"gpt-4-32k",
|
||||
"gpt-4o-2024-05-13",
|
||||
"gpt-4-0613",
|
||||
"gpt-4-32k-0613",
|
||||
]
|
||||
|
||||
GPT4oMINI_MODEL_LIST = [
|
||||
"gpt-4o-mini",
|
||||
"gpt-4o-mini-2024-07-18",
|
||||
]
|
||||
GPT4o_MODEL_LIST = [
|
||||
"gpt-4o",
|
||||
"gpt-4o-2024-05-13",
|
||||
"gpt-4o-2024-08-06",
|
||||
"chatgpt-4o-latest",
|
||||
]
|
||||
|
||||
|
||||
class ChatGPTAPI(Base):
|
||||
DEFAULT_PROMPT = "Please help me to translate,`{text}` to {language}, please return only translated content not include the origin text"
|
||||
@ -43,6 +61,8 @@ class ChatGPTAPI(Base):
|
||||
prompt_template=None,
|
||||
prompt_sys_msg=None,
|
||||
temperature=1.0,
|
||||
context_flag=False,
|
||||
context_paragraph_limit=0,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(key, language)
|
||||
@ -67,6 +87,18 @@ class ChatGPTAPI(Base):
|
||||
self.deployment_id = None
|
||||
self.temperature = temperature
|
||||
self.model_list = None
|
||||
self.context_flag = context_flag
|
||||
self.context_list = []
|
||||
self.context_translated_list = []
|
||||
if context_paragraph_limit > 0:
|
||||
# not set by user, use default
|
||||
self.context_paragraph_limit = context_paragraph_limit
|
||||
else:
|
||||
# set by user, use user's value
|
||||
self.context_paragraph_limit = CHATGPT_CONFIG["context_paragraph_limit"]
|
||||
self.batch_text_list = []
|
||||
self.batch_info_cache = None
|
||||
self.result_content_cache = {}
|
||||
|
||||
def rotate_key(self):
|
||||
self.openai_client.api_key = next(self.keys)
|
||||
@ -74,16 +106,36 @@ class ChatGPTAPI(Base):
|
||||
def rotate_model(self):
|
||||
self.model = next(self.model_list)
|
||||
|
||||
def create_chat_completion(self, text):
|
||||
def create_messages(self, text, intermediate_messages=None):
|
||||
content = self.prompt_template.format(
|
||||
text=text, language=self.language, crlf="\n"
|
||||
)
|
||||
|
||||
sys_content = self.system_content or self.prompt_sys_msg.format(crlf="\n")
|
||||
messages = [
|
||||
{"role": "system", "content": sys_content},
|
||||
{"role": "user", "content": content},
|
||||
]
|
||||
|
||||
if intermediate_messages:
|
||||
messages.extend(intermediate_messages)
|
||||
|
||||
messages.append({"role": "user", "content": content})
|
||||
return messages
|
||||
|
||||
def create_context_messages(self):
|
||||
messages = []
|
||||
if self.context_flag:
|
||||
messages.append({"role": "user", "content": "\n".join(self.context_list)})
|
||||
messages.append(
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "\n".join(self.context_translated_list),
|
||||
}
|
||||
)
|
||||
return messages
|
||||
|
||||
def create_chat_completion(self, text):
|
||||
messages = self.create_messages(text, self.create_context_messages())
|
||||
completion = self.openai_client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
@ -104,8 +156,20 @@ class ChatGPTAPI(Base):
|
||||
else:
|
||||
t_text = ""
|
||||
|
||||
if self.context_flag:
|
||||
self.save_context(text, t_text)
|
||||
|
||||
return t_text
|
||||
|
||||
def save_context(self, text, t_text):
|
||||
if self.context_paragraph_limit > 0:
|
||||
self.context_list.append(text)
|
||||
self.context_translated_list.append(t_text)
|
||||
# Remove the oldest context
|
||||
if len(self.context_list) > self.context_paragraph_limit:
|
||||
self.context_list.pop(0)
|
||||
self.context_translated_list.pop(0)
|
||||
|
||||
def translate(self, text, needprint=True):
|
||||
start_time = time.time()
|
||||
# todo: Determine whether to print according to the cli option
|
||||
@ -334,7 +398,252 @@ class ChatGPTAPI(Base):
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
|
||||
def set_gpt4omini_models(self):
|
||||
# for issue #375 azure can not use model list
|
||||
if self.deployment_id:
|
||||
self.model_list = cycle(["gpt-4o-mini"])
|
||||
else:
|
||||
my_model_list = [
|
||||
i["id"] for i in self.openai_client.models.list().model_dump()["data"]
|
||||
]
|
||||
model_list = list(set(my_model_list) & set(GPT4oMINI_MODEL_LIST))
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
|
||||
def set_gpt4o_models(self):
|
||||
# for issue #375 azure can not use model list
|
||||
if self.deployment_id:
|
||||
self.model_list = cycle(["gpt-4o"])
|
||||
else:
|
||||
my_model_list = [
|
||||
i["id"] for i in self.openai_client.models.list().model_dump()["data"]
|
||||
]
|
||||
model_list = list(set(my_model_list) & set(GPT4o_MODEL_LIST))
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
|
||||
def set_model_list(self, model_list):
|
||||
model_list = list(set(model_list))
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
|
||||
def batch_init(self, book_name):
|
||||
self.book_name = self.sanitize_book_name(book_name)
|
||||
|
||||
def add_to_batch_translate_queue(self, book_index, text):
|
||||
self.batch_text_list.append({"book_index": book_index, "text": text})
|
||||
|
||||
def sanitize_book_name(self, book_name):
|
||||
# Replace any characters that are not alphanumeric, underscore, hyphen, or dot with an underscore
|
||||
sanitized_book_name = re.sub(r"[^\w\-_\.]", "_", book_name)
|
||||
# Remove leading and trailing underscores and dots
|
||||
sanitized_book_name = sanitized_book_name.strip("._")
|
||||
return sanitized_book_name
|
||||
|
||||
def batch_metadata_file_path(self):
|
||||
return os.path.join(os.getcwd(), "batch_files", f"{self.book_name}_info.json")
|
||||
|
||||
def batch_dir(self):
|
||||
return os.path.join(os.getcwd(), "batch_files", self.book_name)
|
||||
|
||||
def custom_id(self, book_index):
|
||||
return f"{self.book_name}-{book_index}"
|
||||
|
||||
def is_completed_batch(self):
|
||||
batch_metadata_file_path = self.batch_metadata_file_path()
|
||||
|
||||
if not os.path.exists(batch_metadata_file_path):
|
||||
print("Batch result file does not exist")
|
||||
raise Exception("Batch result file does not exist")
|
||||
|
||||
with open(batch_metadata_file_path, "r", encoding="utf-8") as f:
|
||||
batch_info = json.load(f)
|
||||
|
||||
for batch_file in batch_info["batch_files"]:
|
||||
batch_status = self.check_batch_status(batch_file["batch_id"])
|
||||
if batch_status.status != "completed":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def batch_translate(self, book_index):
|
||||
if self.batch_info_cache is None:
|
||||
batch_metadata_file_path = self.batch_metadata_file_path()
|
||||
with open(batch_metadata_file_path, "r", encoding="utf-8") as f:
|
||||
self.batch_info_cache = json.load(f)
|
||||
|
||||
batch_info = self.batch_info_cache
|
||||
target_batch = None
|
||||
for batch in batch_info["batch_files"]:
|
||||
if batch["start_index"] <= book_index < batch["end_index"]:
|
||||
target_batch = batch
|
||||
break
|
||||
|
||||
if not target_batch:
|
||||
raise ValueError(f"No batch found for book_index {book_index}")
|
||||
|
||||
if target_batch["batch_id"] in self.result_content_cache:
|
||||
result_content = self.result_content_cache[target_batch["batch_id"]]
|
||||
else:
|
||||
batch_status = self.check_batch_status(target_batch["batch_id"])
|
||||
if batch_status.output_file_id is None:
|
||||
raise ValueError(f"Batch {target_batch['batch_id']} is not completed")
|
||||
result_content = self.get_batch_result(batch_status.output_file_id)
|
||||
self.result_content_cache[target_batch["batch_id"]] = result_content
|
||||
|
||||
result_lines = result_content.text.split("\n")
|
||||
custom_id = self.custom_id(book_index)
|
||||
for line in result_lines:
|
||||
if line.strip():
|
||||
result = json.loads(line)
|
||||
if result["custom_id"] == custom_id:
|
||||
return result["response"]["body"]["choices"][0]["message"][
|
||||
"content"
|
||||
]
|
||||
|
||||
raise ValueError(f"No result found for custom_id {custom_id}")
|
||||
|
||||
def create_batch_context_messages(self, index):
|
||||
messages = []
|
||||
if self.context_flag:
|
||||
if index % CHATGPT_CONFIG[
|
||||
"batch_context_update_interval"
|
||||
] == 0 or not hasattr(self, "cached_context_messages"):
|
||||
context_messages = []
|
||||
for i in range(index - 1, -1, -1):
|
||||
item = self.batch_text_list[i]
|
||||
if len(item["text"].split()) >= 100:
|
||||
context_messages.append(item["text"])
|
||||
if len(context_messages) == self.context_paragraph_limit:
|
||||
break
|
||||
|
||||
if len(context_messages) == self.context_paragraph_limit:
|
||||
print("Creating cached context messages")
|
||||
self.cached_context_messages = [
|
||||
{"role": "user", "content": "\n".join(context_messages)},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": self.get_translation(
|
||||
"\n".join(context_messages)
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
if hasattr(self, "cached_context_messages"):
|
||||
messages.extend(self.cached_context_messages)
|
||||
|
||||
return messages
|
||||
|
||||
def make_batch_request(self, book_index, text):
|
||||
messages = self.create_messages(
|
||||
text, self.create_batch_context_messages(book_index)
|
||||
)
|
||||
return {
|
||||
"custom_id": self.custom_id(book_index),
|
||||
"method": "POST",
|
||||
"url": "/v1/chat/completions",
|
||||
"body": {
|
||||
# model shuould not be rotate
|
||||
"model": self.batch_model,
|
||||
"messages": messages,
|
||||
"temperature": self.temperature,
|
||||
},
|
||||
}
|
||||
|
||||
def create_batch_files(self, dest_file_path):
|
||||
file_paths = []
|
||||
# max request 50,000 and max size 100MB
|
||||
lines_per_file = 40000
|
||||
current_file = 0
|
||||
|
||||
for i in range(0, len(self.batch_text_list), lines_per_file):
|
||||
current_file += 1
|
||||
file_path = os.path.join(dest_file_path, f"{current_file}.jsonl")
|
||||
start_index = i
|
||||
end_index = i + lines_per_file
|
||||
|
||||
# TODO: Split the file if it exceeds 100MB
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
for text in self.batch_text_list[i : i + lines_per_file]:
|
||||
batch_req = self.make_batch_request(
|
||||
text["book_index"], text["text"]
|
||||
)
|
||||
json.dump(batch_req, f, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
file_paths.append(
|
||||
{
|
||||
"file_path": file_path,
|
||||
"start_index": start_index,
|
||||
"end_index": end_index,
|
||||
}
|
||||
)
|
||||
|
||||
return file_paths
|
||||
|
||||
def batch(self):
|
||||
self.rotate_model()
|
||||
self.batch_model = self.model
|
||||
# current working directory
|
||||
batch_dir = self.batch_dir()
|
||||
batch_metadata_file_path = self.batch_metadata_file_path()
|
||||
# cleanup batch dir and result file
|
||||
if os.path.exists(batch_dir):
|
||||
shutil.rmtree(batch_dir)
|
||||
if os.path.exists(batch_metadata_file_path):
|
||||
os.remove(batch_metadata_file_path)
|
||||
os.makedirs(batch_dir, exist_ok=True)
|
||||
# batch execute
|
||||
batch_files = self.create_batch_files(batch_dir)
|
||||
batch_info = []
|
||||
for batch_file in batch_files:
|
||||
file_id = self.upload_batch_file(batch_file["file_path"])
|
||||
batch = self.batch_execute(file_id)
|
||||
batch_info.append(
|
||||
self.create_batch_info(
|
||||
file_id, batch, batch_file["start_index"], batch_file["end_index"]
|
||||
)
|
||||
)
|
||||
# save batch info
|
||||
batch_info_json = {
|
||||
"book_id": self.book_name,
|
||||
"batch_date": time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"batch_files": batch_info,
|
||||
}
|
||||
with open(batch_metadata_file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(batch_info_json, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def create_batch_info(self, file_id, batch, start_index, end_index):
|
||||
return {
|
||||
"input_file_id": file_id,
|
||||
"batch_id": batch.id,
|
||||
"start_index": start_index,
|
||||
"end_index": end_index,
|
||||
"prefix": self.book_name,
|
||||
}
|
||||
|
||||
def upload_batch_file(self, file_path):
|
||||
batch_input_file = self.openai_client.files.create(
|
||||
file=open(file_path, "rb"), purpose="batch"
|
||||
)
|
||||
return batch_input_file.id
|
||||
|
||||
def batch_execute(self, file_id):
|
||||
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
res = self.openai_client.batches.create(
|
||||
input_file_id=file_id,
|
||||
endpoint="/v1/chat/completions",
|
||||
completion_window="24h",
|
||||
metadata={
|
||||
"description": f"Batch job for {self.book_name} at {current_time}"
|
||||
},
|
||||
)
|
||||
if res.errors:
|
||||
print(res.errors)
|
||||
raise Exception(f"Batch execution failed: {res.errors}")
|
||||
return res
|
||||
|
||||
def check_batch_status(self, batch_id):
|
||||
return self.openai_client.batches.retrieve(batch_id)
|
||||
|
||||
def get_batch_result(self, output_file_id):
|
||||
return self.openai_client.files.content(output_file_id)
|
||||
|
@ -1,5 +1,7 @@
|
||||
import re
|
||||
import time
|
||||
from os import environ
|
||||
from itertools import cycle
|
||||
|
||||
import google.generativeai as genai
|
||||
from google.generativeai.types.generation_types import (
|
||||
@ -11,23 +13,36 @@ from rich import print
|
||||
from .base_translator import Base
|
||||
|
||||
generation_config = {
|
||||
"temperature": 0.7,
|
||||
"temperature": 1.0,
|
||||
"top_p": 1,
|
||||
"top_k": 1,
|
||||
"max_output_tokens": 2048,
|
||||
"max_output_tokens": 8192,
|
||||
}
|
||||
|
||||
safety_settings = [
|
||||
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
|
||||
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
|
||||
{
|
||||
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
||||
"threshold": "BLOCK_MEDIUM_AND_ABOVE",
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||
"threshold": "BLOCK_MEDIUM_AND_ABOVE",
|
||||
},
|
||||
safety_settings = {
|
||||
"HATE": "BLOCK_NONE",
|
||||
"HARASSMENT": "BLOCK_NONE",
|
||||
"SEXUAL": "BLOCK_NONE",
|
||||
"DANGEROUS": "BLOCK_NONE",
|
||||
}
|
||||
|
||||
PROMPT_ENV_MAP = {
|
||||
"user": "BBM_GEMINIAPI_USER_MSG_TEMPLATE",
|
||||
"system": "BBM_GEMINIAPI_SYS_MSG",
|
||||
}
|
||||
|
||||
GEMINIPRO_MODEL_LIST = [
|
||||
"gemini-1.5-pro",
|
||||
"gemini-1.5-pro-latest",
|
||||
"gemini-1.5-pro-001",
|
||||
"gemini-1.5-pro-002",
|
||||
]
|
||||
|
||||
GEMINIFLASH_MODEL_LIST = [
|
||||
"gemini-1.5-flash",
|
||||
"gemini-1.5-flash-latest",
|
||||
"gemini-1.5-flash-001",
|
||||
"gemini-1.5-flash-002",
|
||||
]
|
||||
|
||||
|
||||
@ -38,20 +53,57 @@ class Gemini(Base):
|
||||
|
||||
DEFAULT_PROMPT = "Please help me to translate,`{text}` to {language}, please return only translated content not include the origin text"
|
||||
|
||||
def __init__(self, key, language, **kwargs) -> None:
|
||||
genai.configure(api_key=key)
|
||||
def __init__(
|
||||
self,
|
||||
key,
|
||||
language,
|
||||
prompt_template=None,
|
||||
prompt_sys_msg=None,
|
||||
context_flag=False,
|
||||
temperature=1.0,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(key, language)
|
||||
self.context_flag = context_flag
|
||||
self.prompt = (
|
||||
prompt_template
|
||||
or environ.get(PROMPT_ENV_MAP["user"])
|
||||
or self.DEFAULT_PROMPT
|
||||
)
|
||||
self.prompt_sys_msg = (
|
||||
prompt_sys_msg
|
||||
or environ.get(PROMPT_ENV_MAP["system"])
|
||||
or None # Allow None, but not empty string
|
||||
)
|
||||
|
||||
genai.configure(api_key=next(self.keys))
|
||||
generation_config["temperature"] = temperature
|
||||
|
||||
def create_convo(self):
|
||||
model = genai.GenerativeModel(
|
||||
model_name="gemini-pro",
|
||||
model_name=self.model,
|
||||
generation_config=generation_config,
|
||||
safety_settings=safety_settings,
|
||||
system_instruction=self.prompt_sys_msg,
|
||||
)
|
||||
self.convo = model.start_chat()
|
||||
# print(model) # Uncomment to debug and inspect the model details.
|
||||
|
||||
def rotate_model(self):
|
||||
self.model = next(self.model_list)
|
||||
self.create_convo()
|
||||
print(f"Using model {self.model}")
|
||||
|
||||
def rotate_key(self):
|
||||
pass
|
||||
genai.configure(api_key=next(self.keys))
|
||||
self.create_convo()
|
||||
|
||||
def translate(self, text):
|
||||
delay = 1
|
||||
exponential_base = 2
|
||||
attempt_count = 0
|
||||
max_attempts = 7
|
||||
|
||||
t_text = ""
|
||||
print(text)
|
||||
# same for caiyun translate src issue #279 gemini for #374
|
||||
@ -60,32 +112,78 @@ class Gemini(Base):
|
||||
if len(text_list) > 1:
|
||||
if text_list[0].isdigit():
|
||||
num = text_list[0]
|
||||
try:
|
||||
self.convo.send_message(
|
||||
self.DEFAULT_PROMPT.format(text=text, language=self.language)
|
||||
)
|
||||
print(text)
|
||||
t_text = self.convo.last.text.strip()
|
||||
except StopCandidateException as e:
|
||||
match = re.search(r'content\s*{\s*parts\s*{\s*text:\s*"([^"]+)"', str(e))
|
||||
if match:
|
||||
t_text = match.group(1)
|
||||
t_text = re.sub(r"\\n", "\n", t_text)
|
||||
else:
|
||||
t_text = "Can not translate"
|
||||
except BlockedPromptException as e:
|
||||
print(str(e))
|
||||
t_text = "Can not translate by SAFETY reason.(因安全问题不能翻译)"
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
t_text = "Can not translate by other reason.(因安全问题不能翻译)"
|
||||
|
||||
if len(self.convo.history) > 10:
|
||||
self.convo.history = self.convo.history[2:]
|
||||
while attempt_count < max_attempts:
|
||||
try:
|
||||
self.convo.send_message(
|
||||
self.prompt.format(text=text, language=self.language)
|
||||
)
|
||||
t_text = self.convo.last.text.strip()
|
||||
break
|
||||
except StopCandidateException as e:
|
||||
print(
|
||||
f"Translation failed due to StopCandidateException: {e} Attempting to switch model..."
|
||||
)
|
||||
self.rotate_model()
|
||||
except BlockedPromptException as e:
|
||||
print(
|
||||
f"Translation failed due to BlockedPromptException: {e} Attempting to switch model..."
|
||||
)
|
||||
self.rotate_model()
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Translation failed due to {type(e).__name__}: {e} Will sleep {delay} seconds"
|
||||
)
|
||||
time.sleep(delay)
|
||||
delay *= exponential_base
|
||||
|
||||
self.rotate_key()
|
||||
if attempt_count >= 1:
|
||||
self.rotate_model()
|
||||
|
||||
attempt_count += 1
|
||||
|
||||
if attempt_count == max_attempts:
|
||||
print(f"Translation failed after {max_attempts} attempts.")
|
||||
return
|
||||
|
||||
if self.context_flag:
|
||||
if len(self.convo.history) > 10:
|
||||
self.convo.history = self.convo.history[2:]
|
||||
else:
|
||||
self.convo.history = []
|
||||
|
||||
print("[bold green]" + re.sub("\n{3,}", "\n\n", t_text) + "[/bold green]")
|
||||
# for limit
|
||||
time.sleep(0.5)
|
||||
# for rate limit(RPM)
|
||||
time.sleep(self.interval)
|
||||
if num:
|
||||
t_text = str(num) + "\n" + t_text
|
||||
return t_text
|
||||
|
||||
def set_interval(self, interval):
|
||||
self.interval = interval
|
||||
|
||||
def set_geminipro_models(self):
|
||||
self.set_models(GEMINIPRO_MODEL_LIST)
|
||||
|
||||
def set_geminiflash_models(self):
|
||||
self.set_models(GEMINIFLASH_MODEL_LIST)
|
||||
|
||||
def set_models(self, allowed_models):
|
||||
available_models = [
|
||||
re.sub(r"^models/", "", i.name) for i in genai.list_models()
|
||||
]
|
||||
model_list = sorted(
|
||||
list(set(available_models) & set(allowed_models)),
|
||||
key=allowed_models.index,
|
||||
)
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
self.rotate_model()
|
||||
|
||||
def set_model_list(self, model_list):
|
||||
# keep the order of input
|
||||
model_list = sorted(list(set(model_list)), key=model_list.index)
|
||||
print(f"Using model list {model_list}")
|
||||
self.model_list = cycle(model_list)
|
||||
self.rotate_model()
|
||||
|
20
book_maker/translator/xai_translator.py
Normal file
20
book_maker/translator/xai_translator.py
Normal file
@ -0,0 +1,20 @@
|
||||
from openai import OpenAI
|
||||
from .chatgptapi_translator import ChatGPTAPI
|
||||
from os import linesep
|
||||
from itertools import cycle
|
||||
|
||||
|
||||
XAI_MODEL_LIST = [
|
||||
"grok-beta",
|
||||
]
|
||||
|
||||
|
||||
class XAIClient(ChatGPTAPI):
|
||||
def __init__(self, key, language, api_base=None, **kwargs) -> None:
|
||||
super().__init__(key, language)
|
||||
self.model_list = XAI_MODEL_LIST
|
||||
self.api_url = str(api_base) if api_base else "https://api.x.ai/v1"
|
||||
self.openai_client = OpenAI(api_key=key, base_url=self.api_url)
|
||||
|
||||
def rotate_model(self):
|
||||
self.model = self.model_list[0]
|
@ -2,7 +2,7 @@
|
||||
## Models
|
||||
`-m, --model <Model>` <br>
|
||||
|
||||
Currently `bbook_maker` supports these models: `chatgptapi` , `gpt3` , `google` , `caiyun` , `deepl` , `deeplfree` , `gpt4` , `claude` , `customapi`.
|
||||
Currently `bbook_maker` supports these models: `chatgptapi` , `gpt3` , `google` , `caiyun` , `deepl` , `deeplfree` , `gpt4` , `gpt4omini` , `claude` , `customapi`.
|
||||
Default model is `chatgptapi` .
|
||||
|
||||
### OPENAI models
|
||||
|
165
pdm.lock
generated
165
pdm.lock
generated
@ -112,7 +112,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.25.7"
|
||||
version = "0.26.1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "The official Python library for the anthropic API"
|
||||
groups = ["default"]
|
||||
@ -120,14 +120,15 @@ dependencies = [
|
||||
"anyio<5,>=3.5.0",
|
||||
"distro<2,>=1.7.0",
|
||||
"httpx<1,>=0.23.0",
|
||||
"jiter<1,>=0.1.0",
|
||||
"pydantic<3,>=1.9.0",
|
||||
"sniffio",
|
||||
"tokenizers>=0.13.0",
|
||||
"typing-extensions<5,>=4.7",
|
||||
]
|
||||
files = [
|
||||
{file = "anthropic-0.25.7-py3-none-any.whl", hash = "sha256:419a276eb20cfb7ddaac03c7e28e4e12df3ace71bcf33071a68c9a03c0dfcbdd"},
|
||||
{file = "anthropic-0.25.7.tar.gz", hash = "sha256:e7de4c8ba8e7e8248ad7f05ed9176634780b95b67c678d23915d8964c8a26f4e"},
|
||||
{file = "anthropic-0.26.1-py3-none-any.whl", hash = "sha256:2812b9b250b551ed8a1f0a7e6ae3f005654098994f45ebca5b5808bd154c9628"},
|
||||
{file = "anthropic-0.26.1.tar.gz", hash = "sha256:26680ff781a6f678a30a1dccd0743631e602b23a47719439ffdef5335fa167d8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -613,7 +614,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "google-ai-generativelanguage"
|
||||
version = "0.6.2"
|
||||
version = "0.6.4"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Google Ai Generativelanguage API client library"
|
||||
groups = ["default"]
|
||||
@ -624,8 +625,8 @@ dependencies = [
|
||||
"protobuf!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.19.5",
|
||||
]
|
||||
files = [
|
||||
{file = "google-ai-generativelanguage-0.6.2.tar.gz", hash = "sha256:308791ac3b9dad015b359172970739aa3753dd542142a416d07f9fa047e22386"},
|
||||
{file = "google_ai_generativelanguage-0.6.2-py3-none-any.whl", hash = "sha256:bf84c34c641570d7e8a1f2e6901e6771af1438f2ee8307d1801fd43585f9b1c6"},
|
||||
{file = "google-ai-generativelanguage-0.6.4.tar.gz", hash = "sha256:1750848c12af96cb24ae1c3dd05e4bfe24867dc4577009ed03e1042d8421e874"},
|
||||
{file = "google_ai_generativelanguage-0.6.4-py3-none-any.whl", hash = "sha256:730e471aa549797118fb1c88421ba1957741433ada575cf5dd08d3aebf903ab1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -715,12 +716,12 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "google-generativeai"
|
||||
version = "0.5.2"
|
||||
version = "0.5.4"
|
||||
requires_python = ">=3.9"
|
||||
summary = "Google Generative AI High level API client library and tools."
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"google-ai-generativelanguage==0.6.2",
|
||||
"google-ai-generativelanguage==0.6.4",
|
||||
"google-api-core",
|
||||
"google-api-python-client",
|
||||
"google-auth>=2.15.0",
|
||||
@ -730,7 +731,7 @@ dependencies = [
|
||||
"typing-extensions",
|
||||
]
|
||||
files = [
|
||||
{file = "google_generativeai-0.5.2-py3-none-any.whl", hash = "sha256:56f39485a0a673c93c21ec31c17809cc6a964193fb77b7ce809ad15d0dd72d7b"},
|
||||
{file = "google_generativeai-0.5.4-py3-none-any.whl", hash = "sha256:036d63ee35e7c8aedceda4f81c390a5102808af09ff3a6e57e27ed0be0708f3c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -749,7 +750,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "groq"
|
||||
version = "0.5.0"
|
||||
version = "0.8.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "The official Python library for the groq API"
|
||||
groups = ["default"]
|
||||
@ -762,8 +763,8 @@ dependencies = [
|
||||
"typing-extensions<5,>=4.7",
|
||||
]
|
||||
files = [
|
||||
{file = "groq-0.5.0-py3-none-any.whl", hash = "sha256:a7e6be1118bcdfea3ed071ec00f505a34d4e6ec28c435adb5a5afd33545683a1"},
|
||||
{file = "groq-0.5.0.tar.gz", hash = "sha256:d476cdc3383b45d2a4dc1876142a9542e663ea1029f9e07a05de24f895cae48c"},
|
||||
{file = "groq-0.8.0-py3-none-any.whl", hash = "sha256:f5e4e892d45001241a930db451e633ca1f0007e3f749deaa5d7360062fcd61e3"},
|
||||
{file = "groq-0.8.0.tar.gz", hash = "sha256:37ceb2f706bd516d0bfcac8e89048a24b375172987a0d6bd9efb521c54f6deff"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -963,6 +964,64 @@ files = [
|
||||
{file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiter"
|
||||
version = "0.4.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Fast iterable JSON parser."
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "jiter-0.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4aa6226d82a4a4505078c0bd5947bad65399635fc5cd4b226512e41753624edf"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:947111ac906740a948e7b63799481acd3d5ef666ccb178d146e25718640b7408"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69572ffb4e84ae289a7422b9af4ea123cae2ce0772228859b37d4b26b4bc92ea"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba6046cbb5d1baa5a781b846f7e5438596a332f249a857d63f86ef5d1d9563b0"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4f346e54602782e66d07df0d1c7389384fd93680052ed6170da2c6dc758409e"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49110ce693f07e97d61089d894cea05a0b9894d5ccc6ac6fc583028726c8c8af"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e358df6fd129f3a4e087539f086355ad0107e5da16dbc8bc857d94222eaeed5"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb852ca39a48f3c049def56f0d1771b32e948e4f429a782d14ef4cc64cfd26e"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:44dc045decb2545bffe2da04ea4c36d9438d3f3d49fc47ed423ea75c352b712e"},
|
||||
{file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:413adb15372ac63db04373240f40925788e4282c997eeafc2040530049a0a599"},
|
||||
{file = "jiter-0.4.0-cp310-none-win32.whl", hash = "sha256:0b48ea71673a97b897e4b94bbc871e62495a5a85f836c9f90712a4c70aa3ef7e"},
|
||||
{file = "jiter-0.4.0-cp310-none-win_amd64.whl", hash = "sha256:6a1c84b44afafaf0ba6223679cf17af664b889da14da31d8af3595fd977d96fa"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b2cc498345fa37ca23fbc20271a553aa46e6eb00924600f49b7dc4b2aa8952ee"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69f7221ac09ab421abf04f89942026868297c568133998fb181bcf435760cbf3"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7d01c52f3e5a56ae73af36bd13797dd1a56711eb522748e5e84d15425b3f10"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:39be97d5ce0c4d0dae28c23c03a0af0501a725589427e99763f99c42e18aa402"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eac2ed1ec1e577b92b7ea2d4e6de8aec0c1164defd8af8affdc8ec0f0ec2904a"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6258837d184c92c9cb91c983c310ad7269d41afb49d34f00ca9246e073943a03"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c2a77b066bf17a4d021e238e8351058cfa56b90ac04f2522d120dc64ea055"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2df939f792c7a40e55f36700417db551b9f6b84d348990fa0f2c608adeb1f11b"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cb1b09b16d40cf9ba1d11ba11e5b96ad29286a6a1c4ad5e6a2aef5e352a89f5d"},
|
||||
{file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0efb4208889ebdbf933bf08dbcbc16e64ffd34c8e2b28044ee142789a9dc3a67"},
|
||||
{file = "jiter-0.4.0-cp311-none-win32.whl", hash = "sha256:20545ac1b68e7e5b066a1e8347840c9cebdd02ace65faae2e655fc02ec5c915c"},
|
||||
{file = "jiter-0.4.0-cp311-none-win_amd64.whl", hash = "sha256:6b300f9887c8e4431cd03a974ea3e4f9958885636003c3864220a9b2d2f8462b"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:923432a0563bbae404ff25bb010e348514a69bfab979f2f8119b23b625dbf6d9"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab8bb0ec8b97cec4422dc8b37b525442d969244488c805b834609ab0ccd788e2"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b857adb127b9c533907226791eafa79c5038c3eb5a477984994bf7c4715ba518"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2609cc0d1d8d470e921ff9a604afeb4c701bbe13e00bd9834d5aa6e7ea732a9b"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d39e99f8b7df46a119b6f84321f6ba01f16fa46abfa765d44c05c486d8e66829"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56de8b518ebfe76a70f856741f6de248ce396c50a87acef827b6e8388e3a502d"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488b7e777be47f67ce1a1f8f8eb907f9bbd81af5c03784a9bab09d025c250233"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ea35e0ecbb5dadd457855eb980dcc548c14cf5341bcd22a43814cb56f2bcc79"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e1a9e9ee69c80b63951c93226b68d0e955953f64fe758bad2afe7ef7f9016af9"},
|
||||
{file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:78e2f3cc2a32a21d43ccc5afcf66f5d17e827ccc4e6d21c0b353bdad2c7dcc9c"},
|
||||
{file = "jiter-0.4.0-cp312-none-win32.whl", hash = "sha256:eeaa7a2b47a99f4ebbb4142bb58b95617e09f24c87570f6a57d2770687c9ddbe"},
|
||||
{file = "jiter-0.4.0-cp312-none-win_amd64.whl", hash = "sha256:8d4a78b385b93ff59a67215d26000fcb4789a388fca3730d1b60fab17fc81e3c"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:091e978f4e586a2f1c69bf940d45f4e6a23455877172a0ab7d6de04a3b119299"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79134b2d601309bcbe3304a262d7d228ad61d53c80883231c637773000a6d683"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c471473e0b05058b5d729ff04271b6d45a575ac8bd9948563268c734b380ac7e"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb84b8930da8b32b0b1fdff9817e2c4b47e8981b5647ad11c4975403416e4112"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f2805e28941751ebfe0948596a64cde4cfb9b84bea5282affd020063e659c96"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42ef59f9e513bf081a8b5c5578933ea9c3a63e559e6e3501a3e72edcd456ff5e"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae12e3906f9e565120ab569de261b738e3a1ec50c40e30c67499e4f893e9a8c"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:264dc1324f45a793bc89af4f653225229eb17bca9ec7107dce6c8fb4fe68d20f"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9a1c172ec47d846e25881dfbd52438ddb690da4ea04d185e477abd3db6c32f8a"},
|
||||
{file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ccde31d0bc114aedad0dbd71b7f63ba0f0eecd7ec9ae1926a0ca01c1eb2854e7"},
|
||||
{file = "jiter-0.4.0-cp39-none-win32.whl", hash = "sha256:13139b05792fbc13a0f9a5b4c89823ea0874141decae1b8f693f12bb1d28e061"},
|
||||
{file = "jiter-0.4.0-cp39-none-win_amd64.whl", hash = "sha256:3a729b2631c6d5551a41069697415fee9659c3eadc9ab87369376ba51930cd00"},
|
||||
{file = "jiter-0.4.0.tar.gz", hash = "sha256:68203e02e0419bc3eca717c580c2d8f615aeee1150e2a1fb68d6600a7e52a37c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langdetect"
|
||||
version = "1.0.9"
|
||||
@ -977,7 +1036,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "litellm"
|
||||
version = "1.35.38"
|
||||
version = "1.38.10"
|
||||
requires_python = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
|
||||
summary = "Library to easily interface with LLM API providers"
|
||||
groups = ["default"]
|
||||
@ -986,15 +1045,15 @@ dependencies = [
|
||||
"click",
|
||||
"importlib-metadata>=6.8.0",
|
||||
"jinja2<4.0.0,>=3.1.2",
|
||||
"openai>=1.0.0",
|
||||
"openai>=1.27.0",
|
||||
"python-dotenv>=0.2.0",
|
||||
"requests<3.0.0,>=2.31.0",
|
||||
"tiktoken>=0.4.0",
|
||||
"tokenizers",
|
||||
]
|
||||
files = [
|
||||
{file = "litellm-1.35.38-py3-none-any.whl", hash = "sha256:79ab3403c945b340a751d889cf49030fee050487dff6294a21fb9586c49e3faf"},
|
||||
{file = "litellm-1.35.38.tar.gz", hash = "sha256:1a0b195c74d45ba0c2391c5be533c211ee1bcdba6be09e6950037432f62f79ea"},
|
||||
{file = "litellm-1.38.10-py3-none-any.whl", hash = "sha256:4d33465eacde566832b9d7aa7677476e61aa7ba4ec26631fb1c8411c87219ed1"},
|
||||
{file = "litellm-1.38.10.tar.gz", hash = "sha256:1a0b3088fe4b072f367343a7d7d25e4c5f9990975d9ee7dbf21f3b25ff046bb0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1263,7 +1322,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.25.2"
|
||||
version = "1.30.3"
|
||||
requires_python = ">=3.7.1"
|
||||
summary = "The official Python library for the openai API"
|
||||
groups = ["default"]
|
||||
@ -1277,8 +1336,8 @@ dependencies = [
|
||||
"typing-extensions<5,>=4.7",
|
||||
]
|
||||
files = [
|
||||
{file = "openai-1.25.2-py3-none-any.whl", hash = "sha256:8df66384343e81ae49f5b9ee7d2d67df3b98d578d1e3a50994111e1c6062bf5e"},
|
||||
{file = "openai-1.25.2.tar.gz", hash = "sha256:a2b4a6ae9afb8b5578b5b69315ecc6c01564d4c8dbe18fe2375bb9547ff5038c"},
|
||||
{file = "openai-1.30.3-py3-none-any.whl", hash = "sha256:f88119c8a848998be533c71ab8aa832446fa72b7ddbc70917c3f5886dc132051"},
|
||||
{file = "openai-1.30.3.tar.gz", hash = "sha256:8e1bcdca2b96fe3636ab522fa153d88efde1b702d12ec32f1c73e9553ff93f45"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1618,8 +1677,8 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.31.0"
|
||||
requires_python = ">=3.7"
|
||||
version = "2.32.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Python HTTP for Humans."
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
@ -1629,8 +1688,8 @@ dependencies = [
|
||||
"urllib3<3,>=1.21.1",
|
||||
]
|
||||
files = [
|
||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||
{file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"},
|
||||
{file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1708,7 +1767,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tiktoken"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "tiktoken is a fast BPE tokeniser for use with OpenAI's models"
|
||||
groups = ["default"]
|
||||
@ -1717,35 +1776,35 @@ dependencies = [
|
||||
"requests>=2.26.0",
|
||||
]
|
||||
files = [
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:277de84ccd8fa12730a6b4067456e5cf72fef6300bea61d506c09e45658d41ac"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c44433f658064463650d61387623735641dcc4b6c999ca30bc0f8ba3fccaf5c"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afb9a2a866ae6eef1995ab656744287a5ac95acc7e0491c33fad54d053288ad3"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c62c05b3109fefca26fedb2820452a050074ad8e5ad9803f4652977778177d9f"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ef917fad0bccda07bfbad835525bbed5f3ab97a8a3e66526e48cdc3e7beacf7"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e095131ab6092d0769a2fda85aa260c7c383072daec599ba9d8b149d2a3f4d8b"},
|
||||
{file = "tiktoken-0.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:05b344c61779f815038292a19a0c6eb7098b63c8f865ff205abb9ea1b656030e"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cefb9870fb55dca9e450e54dbf61f904aab9180ff6fe568b61f4db9564e78871"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:702950d33d8cabc039845674107d2e6dcabbbb0990ef350f640661368df481bb"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d49d076058f23254f2aff9af603863c5c5f9ab095bc896bceed04f8f0b013a"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:430bc4e650a2d23a789dc2cdca3b9e5e7eb3cd3935168d97d43518cbb1f9a911"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:293cb8669757301a3019a12d6770bd55bec38a4d3ee9978ddbe599d68976aca7"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bd1a288b7903aadc054b0e16ea78e3171f70b670e7372432298c686ebf9dd47"},
|
||||
{file = "tiktoken-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac76e000183e3b749634968a45c7169b351e99936ef46f0d2353cd0d46c3118d"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17cc8a4a3245ab7d935c83a2db6bb71619099d7284b884f4b2aea4c74f2f83e3"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:284aebcccffe1bba0d6571651317df6a5b376ff6cfed5aeb800c55df44c78177"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c1a3a5d33846f8cd9dd3b7897c1d45722f48625a587f8e6f3d3e85080559be8"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6318b2bb2337f38ee954fd5efa82632c6e5ced1d52a671370fa4b2eff1355e91"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f5f0f2ed67ba16373f9a6013b68da298096b27cd4e1cf276d2d3868b5c7efd1"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:75af4c0b16609c2ad02581f3cdcd1fb698c7565091370bf6c0cf8624ffaba6dc"},
|
||||
{file = "tiktoken-0.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:45577faf9a9d383b8fd683e313cf6df88b6076c034f0a16da243bb1c139340c3"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:47fdcfe11bd55376785a6aea8ad1db967db7f66ea81aed5c43fad497521819a4"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb7d2ccbf1a7784810aff6b80b4012fb42c6fc37eaa68cb3b553801a5cc2d1fc"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ccb7a111ee76af5d876a729a347f8747d5ad548e1487eeea90eaf58894b3138"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2048e1086b48e3c8c6e2ceeac866561374cd57a84622fa49a6b245ffecb7744"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07f229a5eb250b6403a61200199cecf0aac4aa23c3ecc1c11c1ca002cbb8f159"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:432aa3be8436177b0db5a2b3e7cc28fd6c693f783b2f8722539ba16a867d0c6a"},
|
||||
{file = "tiktoken-0.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:8bfe8a19c8b5c40d121ee7938cd9c6a278e5b97dc035fd61714b4f0399d2f7a1"},
|
||||
{file = "tiktoken-0.6.0.tar.gz", hash = "sha256:ace62a4ede83c75b0374a2ddfa4b76903cf483e9cb06247f566be3bf14e6beed"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5"},
|
||||
{file = "tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89"},
|
||||
{file = "tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97"},
|
||||
{file = "tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cabc6dc77460df44ec5b879e68692c63551ae4fae7460dd4ff17181df75f1db7"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d57f29171255f74c0aeacd0651e29aa47dff6f070cb9f35ebc14c82278f3b25"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ee92776fdbb3efa02a83f968c19d4997a55c8e9ce7be821ceee04a1d1ee149c"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e215292e99cb41fbc96988ef62ea63bb0ce1e15f2c147a61acc319f8b4cbe5bf"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a81bac94769cab437dd3ab0b8a4bc4e0f9cf6835bcaa88de71f39af1791727a"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6d73ea93e91d5ca771256dfc9d1d29f5a554b83821a1dc0891987636e0ae226"},
|
||||
{file = "tiktoken-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:2bcb28ddf79ffa424f171dfeef9a4daff61a94c631ca6813f43967cb263b83b9"},
|
||||
{file = "tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4,7 +4,7 @@
|
||||
aiohttp==3.9.5
|
||||
aiosignal==1.3.1
|
||||
annotated-types==0.6.0
|
||||
anthropic==0.25.7
|
||||
anthropic==0.26.1
|
||||
anyio==4.3.0
|
||||
async-timeout==4.0.3; python_version < "3.11"
|
||||
attrs==23.2.0
|
||||
@ -25,14 +25,14 @@ exceptiongroup==1.2.1; python_version < "3.11"
|
||||
filelock==3.14.0
|
||||
frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
google-ai-generativelanguage==0.6.2
|
||||
google-api-core==2.19.0
|
||||
google-api-python-client==2.127.0
|
||||
google-auth==2.29.0
|
||||
google-ai-generativelanguage==0.6.10
|
||||
google-api-core==2.21.0
|
||||
google-api-python-client==2.149.0
|
||||
google-auth==2.35.0
|
||||
google-auth-httplib2==0.2.0
|
||||
google-generativeai==0.5.2
|
||||
googleapis-common-protos==1.63.0
|
||||
groq==0.5.0
|
||||
google-generativeai==0.8.3
|
||||
googleapis-common-protos==1.65.0
|
||||
groq==0.8.0
|
||||
grpcio==1.63.0
|
||||
grpcio-status==1.62.2
|
||||
h11==0.14.0
|
||||
@ -43,14 +43,15 @@ huggingface-hub==0.22.2
|
||||
idna==3.7
|
||||
importlib-metadata==7.1.0
|
||||
jinja2==3.1.3
|
||||
jiter==0.4.0
|
||||
langdetect==1.0.9
|
||||
litellm==1.35.38
|
||||
litellm==1.38.10
|
||||
lxml==5.2.1
|
||||
markdown-it-py==3.0.0
|
||||
markupsafe==2.1.5
|
||||
mdurl==0.1.2
|
||||
multidict==6.0.5
|
||||
openai==1.25.2
|
||||
openai==1.30.3
|
||||
packaging==24.0
|
||||
proto-plus==1.23.0
|
||||
protobuf==4.25.3
|
||||
@ -65,14 +66,14 @@ pyparsing==3.1.2; python_version > "3.0"
|
||||
python-dotenv==1.0.1
|
||||
pyyaml==6.0.1
|
||||
regex==2024.4.28
|
||||
requests==2.31.0
|
||||
requests==2.32.2
|
||||
rich==13.7.1
|
||||
rsa==4.9
|
||||
six==1.16.0
|
||||
sniffio==1.3.1
|
||||
socksio==1.0.0
|
||||
soupsieve==2.5
|
||||
tiktoken==0.6.0
|
||||
tiktoken==0.7.0
|
||||
tokenizers==0.19.1
|
||||
tqdm==4.66.4
|
||||
typing-extensions==4.11.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user