diff --git a/.gitignore b/.gitignore
index 38ec6b5e4..e0911cf4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -163,3 +163,5 @@ objdump*
.DS_Store
.aider*
+
+TODO
\ No newline at end of file
diff --git a/config.py b/config.py
index f654054e2..6e636278e 100644
--- a/config.py
+++ b/config.py
@@ -36,8 +36,11 @@
"gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-mini",
"gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt-3.5",
"gpt-4", "gpt-4-32k", "azure-gpt-4", "glm-4", "glm-4v", "glm-3-turbo",
- "gemini-pro", "chatglm3"
+ "gemini-1.5-pro", "chatglm3"
]
+
+EMBEDDING_MODEL = "text-embedding-3-small"
+
# --- --- --- ---
# P.S. 其他可用的模型还包括
# AVAIL_LLM_MODELS = [
@@ -50,6 +53,7 @@
# "claude-3-haiku-20240307","claude-3-sonnet-20240229","claude-3-opus-20240229", "claude-2.1", "claude-instant-1.2",
# "moss", "llama2", "chatglm_onnx", "internlm", "jittorllms_pangualpha", "jittorllms_llama",
# "deepseek-chat" ,"deepseek-coder",
+# "gemini-1.5-flash",
# "yi-34b-chat-0205","yi-34b-chat-200k","yi-large","yi-medium","yi-spark","yi-large-turbo","yi-large-preview",
# ]
# --- --- --- ---
@@ -296,7 +300,7 @@
# 除了连接OpenAI之外,还有哪些场合允许使用代理,请尽量不要修改
WHEN_TO_USE_PROXY = ["Download_LLM", "Download_Gradio_Theme", "Connect_Grobid",
- "Warmup_Modules", "Nougat_Download", "AutoGen"]
+ "Warmup_Modules", "Nougat_Download", "AutoGen", "Connect_OpenAI_Embedding"]
# 启用插件热加载
diff --git a/crazy_functional.py b/crazy_functional.py
index 222ca3253..1d1945098 100644
--- a/crazy_functional.py
+++ b/crazy_functional.py
@@ -5,6 +5,7 @@
def get_crazy_functions():
from crazy_functions.读文章写摘要 import 读文章写摘要
from crazy_functions.生成函数注释 import 批量生成函数注释
+ from crazy_functions.Rag_Interface import Rag问答
from crazy_functions.SourceCode_Analyse import 解析项目本身
from crazy_functions.SourceCode_Analyse import 解析一个Python项目
from crazy_functions.SourceCode_Analyse import 解析一个Matlab项目
@@ -50,6 +51,13 @@ def get_crazy_functions():
from crazy_functions.SourceCode_Comment import 注释Python项目
function_plugins = {
+ "Rag智能召回": {
+ "Group": "对话",
+ "Color": "stop",
+ "AsButton": False,
+ "Info": "将问答数据记录到向量库中,作为长期参考。",
+ "Function": HotReload(Rag问答),
+ },
"虚空终端": {
"Group": "对话|编程|学术|智能体",
"Color": "stop",
diff --git a/crazy_functions/Rag_Interface.py b/crazy_functions/Rag_Interface.py
new file mode 100644
index 000000000..d83d8ca5a
--- /dev/null
+++ b/crazy_functions/Rag_Interface.py
@@ -0,0 +1,95 @@
+from toolbox import CatchException, update_ui, get_conf, get_log_folder, update_ui_lastest_msg
+from crazy_functions.crazy_utils import input_clipping
+from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
+
+VECTOR_STORE_TYPE = "Milvus"
+
+if VECTOR_STORE_TYPE == "Milvus":
+ try:
+ from crazy_functions.rag_fns.milvus_worker import MilvusRagWorker as LlamaIndexRagWorker
+ except:
+ VECTOR_STORE_TYPE = "Simple"
+
+if VECTOR_STORE_TYPE == "Simple":
+ from crazy_functions.rag_fns.llama_index_worker import LlamaIndexRagWorker
+
+
+RAG_WORKER_REGISTER = {}
+
+MAX_HISTORY_ROUND = 5
+MAX_CONTEXT_TOKEN_LIMIT = 4096
+REMEMBER_PREVIEW = 1000
+
+@CatchException
+def Rag问答(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
+
+ # 1. we retrieve rag worker from global context
+ user_name = chatbot.get_user()
+ checkpoint_dir = get_log_folder(user_name, plugin_name='experimental_rag')
+ if user_name in RAG_WORKER_REGISTER:
+ rag_worker = RAG_WORKER_REGISTER[user_name]
+ else:
+ rag_worker = RAG_WORKER_REGISTER[user_name] = LlamaIndexRagWorker(
+ user_name,
+ llm_kwargs,
+ checkpoint_dir=checkpoint_dir,
+ auto_load_checkpoint=True)
+ current_context = f"{VECTOR_STORE_TYPE} @ {checkpoint_dir}"
+ tip = "提示:输入“清空向量数据库”可以清空RAG向量数据库"
+ if txt == "清空向量数据库":
+ chatbot.append([txt, f'正在清空 ({current_context}) ...'])
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
+ rag_worker.purge()
+ yield from update_ui_lastest_msg('已清空', chatbot, history, delay=0) # 刷新界面
+ return
+
+ chatbot.append([txt, f'正在召回知识 ({current_context}) ...'])
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
+
+ # 2. clip history to reduce token consumption
+ # 2-1. reduce chat round
+ txt_origin = txt
+
+ if len(history) > MAX_HISTORY_ROUND * 2:
+ history = history[-(MAX_HISTORY_ROUND * 2):]
+ txt_clip, history, flags = input_clipping(txt, history, max_token_limit=MAX_CONTEXT_TOKEN_LIMIT, return_clip_flags=True)
+ input_is_clipped_flag = (flags["original_input_len"] != flags["clipped_input_len"])
+
+ # 2-2. if input is clipped, add input to vector store before retrieve
+ if input_is_clipped_flag:
+ yield from update_ui_lastest_msg('检测到长输入, 正在向量化 ...', chatbot, history, delay=0) # 刷新界面
+ # save input to vector store
+ rag_worker.add_text_to_vector_store(txt_origin)
+ yield from update_ui_lastest_msg('向量化完成 ...', chatbot, history, delay=0) # 刷新界面
+ if len(txt_origin) > REMEMBER_PREVIEW:
+ HALF = REMEMBER_PREVIEW//2
+ i_say_to_remember = txt[:HALF] + f" ...\n...(省略{len(txt_origin)-REMEMBER_PREVIEW}字)...\n... " + txt[-HALF:]
+ if (flags["original_input_len"] - flags["clipped_input_len"]) > HALF:
+ txt_clip = txt_clip + f" ...\n...(省略{len(txt_origin)-len(txt_clip)-HALF}字)...\n... " + txt[-HALF:]
+ else:
+ pass
+ i_say = txt_clip
+ else:
+ i_say_to_remember = i_say = txt_clip
+ else:
+ i_say_to_remember = i_say = txt_clip
+
+ # 3. we search vector store and build prompts
+ nodes = rag_worker.retrieve_from_store_with_query(i_say)
+ prompt = rag_worker.build_prompt(query=i_say, nodes=nodes)
+
+ # 4. it is time to query llms
+ if len(chatbot) != 0: chatbot.pop(-1) # pop temp chat, because we are going to add them again inside `request_gpt_model_in_new_thread_with_ui_alive`
+ model_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
+ inputs=prompt, inputs_show_user=i_say,
+ llm_kwargs=llm_kwargs, chatbot=chatbot, history=history,
+ sys_prompt=system_prompt,
+ retry_times_at_unknown_error=0
+ )
+
+ # 5. remember what has been asked / answered
+ yield from update_ui_lastest_msg(model_say + '' + f'对话记忆中, 请稍等 ({current_context}) ...', chatbot, history, delay=0.5) # 刷新界面
+ rag_worker.remember_qa(i_say_to_remember, model_say)
+ history.extend([i_say, model_say])
+
+ yield from update_ui_lastest_msg(model_say, chatbot, history, delay=0, msg=tip) # 刷新界面
diff --git a/crazy_functions/Social_Helper.py b/crazy_functions/Social_Helper.py
new file mode 100644
index 000000000..9627f9c03
--- /dev/null
+++ b/crazy_functions/Social_Helper.py
@@ -0,0 +1,65 @@
+from toolbox import CatchException, update_ui, get_conf, get_log_folder, update_ui_lastest_msg
+from crazy_functions.crazy_utils import input_clipping
+from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
+import pickle, os
+
+SOCIAL_NETWOK_WORKER_REGISTER = {}
+
+class SocialNetwork():
+ def __init__(self):
+ self.people = []
+
+class SocialNetworkWorker():
+ def __init__(self, user_name, llm_kwargs, auto_load_checkpoint=True, checkpoint_dir=None) -> None:
+ self.user_name = user_name
+ self.checkpoint_dir = checkpoint_dir
+ if auto_load_checkpoint:
+ self.social_network = self.load_from_checkpoint(checkpoint_dir)
+ else:
+ self.social_network = SocialNetwork()
+
+ def does_checkpoint_exist(self, checkpoint_dir=None):
+ import os, glob
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if not os.path.exists(checkpoint_dir): return False
+ if len(glob.glob(os.path.join(checkpoint_dir, "social_network.pkl"))) == 0: return False
+ return True
+
+ def save_to_checkpoint(self, checkpoint_dir=None):
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ with open(os.path.join(checkpoint_dir, 'social_network.pkl'), "wb+") as f:
+ pickle.dump(self.social_network, f)
+ return
+
+ def load_from_checkpoint(self, checkpoint_dir=None):
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if self.does_checkpoint_exist(checkpoint_dir=checkpoint_dir):
+ with open(os.path.join(checkpoint_dir, 'social_network.pkl'), "rb") as f:
+ social_network = pickle.load(f)
+ return social_network
+ else:
+ return SocialNetwork()
+
+
+@CatchException
+def I人助手(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request, num_day=5):
+
+ # 1. we retrieve worker from global context
+ user_name = chatbot.get_user()
+ checkpoint_dir=get_log_folder(user_name, plugin_name='experimental_rag')
+ if user_name in SOCIAL_NETWOK_WORKER_REGISTER:
+ social_network_worker = SOCIAL_NETWOK_WORKER_REGISTER[user_name]
+ else:
+ social_network_worker = SOCIAL_NETWOK_WORKER_REGISTER[user_name] = SocialNetworkWorker(
+ user_name,
+ llm_kwargs,
+ checkpoint_dir=checkpoint_dir,
+ auto_load_checkpoint=True
+ )
+
+ # 2. save
+ social_network_worker.social_network.people.append("张三")
+ social_network_worker.save_to_checkpoint(checkpoint_dir)
+ chatbot.append(["good", "work"])
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
+
diff --git a/crazy_functions/crazy_utils.py b/crazy_functions/crazy_utils.py
index 0cccceebd..d2fe1d3ec 100644
--- a/crazy_functions/crazy_utils.py
+++ b/crazy_functions/crazy_utils.py
@@ -4,7 +4,7 @@
import os
import logging
-def input_clipping(inputs, history, max_token_limit):
+def input_clipping(inputs, history, max_token_limit, return_clip_flags=False):
"""
当输入文本 + 历史文本超出最大限制时,采取措施丢弃一部分文本。
输入:
@@ -20,17 +20,20 @@ def input_clipping(inputs, history, max_token_limit):
enc = model_info["gpt-3.5-turbo"]['tokenizer']
def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
+
mode = 'input-and-history'
# 当 输入部分的token占比 小于 全文的一半时,只裁剪历史
input_token_num = get_token_num(inputs)
+ original_input_len = len(inputs)
if input_token_num < max_token_limit//2:
mode = 'only-history'
max_token_limit = max_token_limit - input_token_num
everything = [inputs] if mode == 'input-and-history' else ['']
everything.extend(history)
- n_token = get_token_num('\n'.join(everything))
+ full_token_num = n_token = get_token_num('\n'.join(everything))
everything_token = [get_token_num(e) for e in everything]
+ everything_token_num = sum(everything_token)
delta = max(everything_token) // 16 # 截断时的颗粒度
while n_token > max_token_limit:
@@ -43,10 +46,24 @@ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
if mode == 'input-and-history':
inputs = everything[0]
+ full_token_num = everything_token_num
else:
- pass
+ full_token_num = everything_token_num + input_token_num
+
history = everything[1:]
- return inputs, history
+
+ flags = {
+ "mode": mode,
+ "original_input_token_num": input_token_num,
+ "original_full_token_num": full_token_num,
+ "original_input_len": original_input_len,
+ "clipped_input_len": len(inputs),
+ }
+
+ if not return_clip_flags:
+ return inputs, history
+ else:
+ return inputs, history, flags
def request_gpt_model_in_new_thread_with_ui_alive(
inputs, inputs_show_user, llm_kwargs,
diff --git a/crazy_functions/rag_fns/llama_index_worker.py b/crazy_functions/rag_fns/llama_index_worker.py
new file mode 100644
index 000000000..7a5599279
--- /dev/null
+++ b/crazy_functions/rag_fns/llama_index_worker.py
@@ -0,0 +1,129 @@
+import llama_index
+import os
+import atexit
+from typing import List
+from llama_index.core import Document
+from llama_index.core.schema import TextNode
+from request_llms.embed_models.openai_embed import OpenAiEmbeddingModel
+from shared_utils.connect_void_terminal import get_chat_default_kwargs
+from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
+from crazy_functions.rag_fns.vector_store_index import GptacVectorStoreIndex
+from llama_index.core.ingestion import run_transformations
+from llama_index.core import PromptTemplate
+from llama_index.core.response_synthesizers import TreeSummarize
+
+DEFAULT_QUERY_GENERATION_PROMPT = """\
+Now, you have context information as below:
+---------------------
+{context_str}
+---------------------
+Answer the user request below (use the context information if necessary, otherwise you can ignore them):
+---------------------
+{query_str}
+"""
+
+QUESTION_ANSWER_RECORD = """\
+{{
+ "type": "This is a previous conversation with the user",
+ "question": "{question}",
+ "answer": "{answer}",
+}}
+"""
+
+
+class SaveLoad():
+
+ def does_checkpoint_exist(self, checkpoint_dir=None):
+ import os, glob
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if not os.path.exists(checkpoint_dir): return False
+ if len(glob.glob(os.path.join(checkpoint_dir, "*.json"))) == 0: return False
+ return True
+
+ def save_to_checkpoint(self, checkpoint_dir=None):
+ print(f'saving vector store to: {checkpoint_dir}')
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ self.vs_index.storage_context.persist(persist_dir=checkpoint_dir)
+
+ def load_from_checkpoint(self, checkpoint_dir=None):
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if self.does_checkpoint_exist(checkpoint_dir=checkpoint_dir):
+ print('loading checkpoint from disk')
+ from llama_index.core import StorageContext, load_index_from_storage
+ storage_context = StorageContext.from_defaults(persist_dir=checkpoint_dir)
+ self.vs_index = load_index_from_storage(storage_context, embed_model=self.embed_model)
+ return self.vs_index
+ else:
+ return self.create_new_vs()
+
+ def create_new_vs(self):
+ return GptacVectorStoreIndex.default_vector_store(embed_model=self.embed_model)
+
+ def purge(self):
+ import shutil
+ shutil.rmtree(self.checkpoint_dir, ignore_errors=True)
+ self.vs_index = self.create_new_vs()
+
+
+class LlamaIndexRagWorker(SaveLoad):
+ def __init__(self, user_name, llm_kwargs, auto_load_checkpoint=True, checkpoint_dir=None) -> None:
+ self.debug_mode = True
+ self.embed_model = OpenAiEmbeddingModel(llm_kwargs)
+ self.user_name = user_name
+ self.checkpoint_dir = checkpoint_dir
+ if auto_load_checkpoint:
+ self.vs_index = self.load_from_checkpoint(checkpoint_dir)
+ else:
+ self.vs_index = self.create_new_vs(checkpoint_dir)
+ atexit.register(lambda: self.save_to_checkpoint(checkpoint_dir))
+
+ def assign_embedding_model(self):
+ pass
+
+ def inspect_vector_store(self):
+ # This function is for debugging
+ self.vs_index.storage_context.index_store.to_dict()
+ docstore = self.vs_index.storage_context.docstore.docs
+ vector_store_preview = "\n".join([ f"{_id} | {tn.text}" for _id, tn in docstore.items() ])
+ print('\n++ --------inspect_vector_store begin--------')
+ print(vector_store_preview)
+ print('oo --------inspect_vector_store end--------')
+ return vector_store_preview
+
+ def add_documents_to_vector_store(self, document_list):
+ documents = [Document(text=t) for t in document_list]
+ documents_nodes = run_transformations(
+ documents, # type: ignore
+ self.vs_index._transformations,
+ show_progress=True
+ )
+ self.vs_index.insert_nodes(documents_nodes)
+ if self.debug_mode: self.inspect_vector_store()
+
+ def add_text_to_vector_store(self, text):
+ node = TextNode(text=text)
+ documents_nodes = run_transformations(
+ [node],
+ self.vs_index._transformations,
+ show_progress=True
+ )
+ self.vs_index.insert_nodes(documents_nodes)
+ if self.debug_mode: self.inspect_vector_store()
+
+ def remember_qa(self, question, answer):
+ formatted_str = QUESTION_ANSWER_RECORD.format(question=question, answer=answer)
+ self.add_text_to_vector_store(formatted_str)
+
+ def retrieve_from_store_with_query(self, query):
+ if self.debug_mode: self.inspect_vector_store()
+ retriever = self.vs_index.as_retriever()
+ return retriever.retrieve(query)
+
+ def build_prompt(self, query, nodes):
+ context_str = self.generate_node_array_preview(nodes)
+ return DEFAULT_QUERY_GENERATION_PROMPT.format(context_str=context_str, query_str=query)
+
+ def generate_node_array_preview(self, nodes):
+ buf = "\n".join(([f"(No.{i+1} | score {n.score:.3f}): {n.text}" for i, n in enumerate(nodes)]))
+ if self.debug_mode: print(buf)
+ return buf
diff --git a/crazy_functions/rag_fns/milvus_worker.py b/crazy_functions/rag_fns/milvus_worker.py
new file mode 100644
index 000000000..4b5b0ad99
--- /dev/null
+++ b/crazy_functions/rag_fns/milvus_worker.py
@@ -0,0 +1,107 @@
+import llama_index
+import os
+import atexit
+from typing import List
+from llama_index.core import Document
+from llama_index.core.schema import TextNode
+from request_llms.embed_models.openai_embed import OpenAiEmbeddingModel
+from shared_utils.connect_void_terminal import get_chat_default_kwargs
+from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
+from crazy_functions.rag_fns.vector_store_index import GptacVectorStoreIndex
+from llama_index.core.ingestion import run_transformations
+from llama_index.core import PromptTemplate
+from llama_index.core.response_synthesizers import TreeSummarize
+from llama_index.core import StorageContext
+from llama_index.vector_stores.milvus import MilvusVectorStore
+from crazy_functions.rag_fns.llama_index_worker import LlamaIndexRagWorker
+
+DEFAULT_QUERY_GENERATION_PROMPT = """\
+Now, you have context information as below:
+---------------------
+{context_str}
+---------------------
+Answer the user request below (use the context information if necessary, otherwise you can ignore them):
+---------------------
+{query_str}
+"""
+
+QUESTION_ANSWER_RECORD = """\
+{{
+ "type": "This is a previous conversation with the user",
+ "question": "{question}",
+ "answer": "{answer}",
+}}
+"""
+
+
+class MilvusSaveLoad():
+
+ def does_checkpoint_exist(self, checkpoint_dir=None):
+ import os, glob
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if not os.path.exists(checkpoint_dir): return False
+ if len(glob.glob(os.path.join(checkpoint_dir, "*.json"))) == 0: return False
+ return True
+
+ def save_to_checkpoint(self, checkpoint_dir=None):
+ print(f'saving vector store to: {checkpoint_dir}')
+ # if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ # self.vs_index.storage_context.persist(persist_dir=checkpoint_dir)
+
+ def load_from_checkpoint(self, checkpoint_dir=None):
+ if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir
+ if self.does_checkpoint_exist(checkpoint_dir=checkpoint_dir):
+ print('loading checkpoint from disk')
+ from llama_index.core import StorageContext, load_index_from_storage
+ storage_context = StorageContext.from_defaults(persist_dir=checkpoint_dir)
+ try:
+ self.vs_index = load_index_from_storage(storage_context, embed_model=self.embed_model)
+ return self.vs_index
+ except:
+ return self.create_new_vs(checkpoint_dir)
+ else:
+ return self.create_new_vs(checkpoint_dir)
+
+ def create_new_vs(self, checkpoint_dir, overwrite=False):
+ vector_store = MilvusVectorStore(
+ uri=os.path.join(checkpoint_dir, "milvus_demo.db"),
+ dim=self.embed_model.embedding_dimension(),
+ overwrite=overwrite
+ )
+ storage_context = StorageContext.from_defaults(vector_store=vector_store)
+ index = GptacVectorStoreIndex.default_vector_store(storage_context=storage_context, embed_model=self.embed_model)
+ return index
+
+ def purge(self):
+ self.vs_index = self.create_new_vs(self.checkpoint_dir, overwrite=True)
+
+class MilvusRagWorker(MilvusSaveLoad, LlamaIndexRagWorker):
+
+ def __init__(self, user_name, llm_kwargs, auto_load_checkpoint=True, checkpoint_dir=None) -> None:
+ self.debug_mode = True
+ self.embed_model = OpenAiEmbeddingModel(llm_kwargs)
+ self.user_name = user_name
+ self.checkpoint_dir = checkpoint_dir
+ if auto_load_checkpoint:
+ self.vs_index = self.load_from_checkpoint(checkpoint_dir)
+ else:
+ self.vs_index = self.create_new_vs(checkpoint_dir)
+ atexit.register(lambda: self.save_to_checkpoint(checkpoint_dir))
+
+ def inspect_vector_store(self):
+ # This function is for debugging
+ try:
+ self.vs_index.storage_context.index_store.to_dict()
+ docstore = self.vs_index.storage_context.docstore.docs
+ if not docstore.items():
+ raise ValueError("cannot inspect")
+ vector_store_preview = "\n".join([ f"{_id} | {tn.text}" for _id, tn in docstore.items() ])
+ except:
+ dummy_retrieve_res: List["NodeWithScore"] = self.vs_index.as_retriever().retrieve(' ')
+ vector_store_preview = "\n".join(
+ [f"{node.id_} | {node.text}" for node in dummy_retrieve_res]
+ )
+ print('\n++ --------inspect_vector_store begin--------')
+ print(vector_store_preview)
+ print('oo --------inspect_vector_store end--------')
+ return vector_store_preview
diff --git a/crazy_functions/rag_fns/vector_store_index.py b/crazy_functions/rag_fns/vector_store_index.py
new file mode 100644
index 000000000..74e8b09df
--- /dev/null
+++ b/crazy_functions/rag_fns/vector_store_index.py
@@ -0,0 +1,58 @@
+from llama_index.core import VectorStoreIndex
+from typing import Any, List, Optional
+
+from llama_index.core.callbacks.base import CallbackManager
+from llama_index.core.schema import TransformComponent
+from llama_index.core.service_context import ServiceContext
+from llama_index.core.settings import (
+ Settings,
+ callback_manager_from_settings_or_context,
+ transformations_from_settings_or_context,
+)
+from llama_index.core.storage.storage_context import StorageContext
+
+
+class GptacVectorStoreIndex(VectorStoreIndex):
+
+ @classmethod
+ def default_vector_store(
+ cls,
+ storage_context: Optional[StorageContext] = None,
+ show_progress: bool = False,
+ callback_manager: Optional[CallbackManager] = None,
+ transformations: Optional[List[TransformComponent]] = None,
+ # deprecated
+ service_context: Optional[ServiceContext] = None,
+ embed_model = None,
+ **kwargs: Any,
+ ):
+ """Create index from documents.
+
+ Args:
+ documents (Optional[Sequence[BaseDocument]]): List of documents to
+ build the index from.
+
+ """
+ storage_context = storage_context or StorageContext.from_defaults()
+ docstore = storage_context.docstore
+ callback_manager = (
+ callback_manager
+ or callback_manager_from_settings_or_context(Settings, service_context)
+ )
+ transformations = transformations or transformations_from_settings_or_context(
+ Settings, service_context
+ )
+
+ with callback_manager.as_trace("index_construction"):
+
+ return cls(
+ nodes=[],
+ storage_context=storage_context,
+ callback_manager=callback_manager,
+ show_progress=show_progress,
+ transformations=transformations,
+ service_context=service_context,
+ embed_model=embed_model,
+ **kwargs,
+ )
+
diff --git a/docs/translate_english.json b/docs/translate_english.json
index f82bfecb9..db5177141 100644
--- a/docs/translate_english.json
+++ b/docs/translate_english.json
@@ -291,7 +291,7 @@
"gradio的inbrowser触发不太稳定": "In-browser triggering of gradio is not very stable",
"回滚代码到原始的浏览器打开函数": "Roll back code to the original browser open function",
"打开浏览器": "Open browser",
- "ChatGPT 学术优化": "ChatGPT academic optimization",
+ "ChatGPT 学术优化": "GPT Academic",
"代码开源和更新": "Code open source and updates",
"地址🚀": "Address 🚀",
"感谢热情的": "Thanks to the enthusiastic",
@@ -3976,5 +3976,234 @@
"为了更灵活地接入vllm多模型管理界面": "To more flexibly access the vllm multi-model management interface",
"读取解析": "Read and parse",
"允许缓存": "Allow caching",
- "Run2 中对 Kaon 鉴别的要求被收紧为 $ \\operatorname{ProbNNk}\\left": "The requirement for Kaon discrimination in Run2 has been tightened to $ \\operatorname{ProbNNk}\\left"
+ "Run2 中对 Kaon 鉴别的要求被收紧为 $ \\operatorname{ProbNNk}\\left": "The requirement for Kaon discrimination in Run2 has been tightened to $ \\operatorname{ProbNNk}\\left",
+ "当前使用人数太多": "Current user count is too high",
+ "提取historyBox信息": "Extract historyBox information",
+ "📚Arxiv论文精细翻译": "Fine translation of 📚Arxiv papers",
+ "检索中": "Searching",
+ "受到限制": "Restricted",
+ "3. 历史输入包含图像": "3. Historical input contains images",
+ "待通过互联网检索的问题": "Questions to be retrieved via the internet",
+ "使用 findall 方法查找所有匹配的 Base64 字符串": "Use findall method to find all matching Base64 strings",
+ "建立文件树": "Build file tree",
+ "经过clip": "Through clip",
+ "增强": "Enhance",
+ "对话存档": "Conversation archive",
+ "网页": "Webpage",
+ "怎么下载相关论文": "How to download related papers",
+ "当前对话是关于 Nginx 的介绍和使用等": "The current conversation is about the introduction and use of Nginx, etc.",
+ "从而提高学术论文检索的精度": "To improve the accuracy of academic paper retrieval",
+ "使用自然语言实现您的想法": "Implement your ideas using natural language",
+ "这样可以保证后续问答能读取到有效的历史记录": "This ensures that subsequent questions and answers can read valid historical records",
+ "生成对比html": "Generate comparison html",
+ "Doc2x API 页数受限": "Doc2x API page count limited",
+ "inputs 本次请求": "Inputs for this request",
+ "有其原问题": "Has its original question",
+ "在线搜索失败": "Online search failed",
+ "选择搜索引擎": "Choose a search engine",
+ "同步已知模型的其他信息": "Synchronize other information of known models",
+ "在线搜索服务": "Online search service",
+ "常规对话": "Regular conversation",
+ "使用正则表达式匹配模式": "Use regular expressions to match patterns",
+ "从而提高网页检索的精度": "To improve the accuracy of webpage retrieval",
+ "GPT-Academic输出文档": "GPT-Academic output document",
+ "/* 小按钮 */": "/* Small button */",
+ "历史记录": "History record",
+ "上传一系列python源文件": "Upload a series of python source files",
+ "仅DALLE3生效": "Only DALLE3 takes effect",
+ "判断给定的单个字符是否是全角字符": "Determine if the given single character is a full-width character",
+ "依次放入每组第一": "Put each group's first one by one",
+ "这部分代码会逐渐移动到common.js中": "This part of the code will gradually move to common.js",
+ "列出机器学习的三种应用": "List three applications of machine learning",
+ "更新主输入区的参数": "Update the parameters of the main input area",
+ "从以上搜索结果中抽取与问题": "Extract from the above search results related to the question",
+ "* 如果解码失败": "* If decoding fails",
+ "如果是已知模型": "If it is a known model",
+ "一": "One",
+ "模型切换时的回调": "Callback when switching models",
+ "加入历史": "Add to history",
+ "压缩结果": "Compress the result",
+ "使用 DALLE2/DALLE3 生成图片 | 输入参数字符串": "Use DALLE2/DALLE3 to generate images | Input parameter string",
+ "搜索分类": "Search category",
+ "获得空的回复": "Get an empty reply",
+ "多模态模型": "Multimodal model",
+ "移除注释": "Remove comments",
+ "对话背景": "Conversation background",
+ "获取需要执行的插件名称": "Get the name of the plugin to be executed",
+ "是否启动语音输入功能": "Whether to enable voice input function",
+ "更新高级参数输入区的参数": "Update the parameters of the advanced parameter input area",
+ "启用多模态能力": "Enable multimodal capabilities",
+ "请根据以上搜索结果回答问题": "Please answer the question based on the above search results",
+ "生成的问题要求指向对象清晰明确": "The generated question requires clear and specific references to the object",
+ "Arxiv论文翻译": "Translation of Arxiv paper",
+ "找不到该模型": "Model not found",
+ "提取匹配的数字部分并转换为整数": "Extract matching numeric parts and convert to integers",
+ "尝试进行搜索优化": "Try to optimize the search",
+ "重新梳理输入参数": "Reorganize input parameters",
+ "存储翻译好的arxiv论文的路径": "Path to store translated arxiv papers",
+ "尽量使用英文": "Use English as much as possible",
+ "插件二级菜单的实现": "Implementation of plugin submenus",
+ "* 增强优化": "Enhanced optimization",
+ "但属于其他用户": "But belongs to another user",
+ "不得有多余字符": "No extra characters allowed",
+ "怎么解决": "How to solve",
+ "根据综合回答问题": "Answer questions comprehensively",
+ "降低温度再试一次": "Lower the temperature and try again",
+ "作为一个网页搜索助手": "As a web search assistant",
+ "支持将文件直接粘贴到输入区": "Support pasting files directly into the input area",
+ "打开新对话": "Open a new conversation",
+ "但位置非法": "But the position is illegal",
+ "会自动读取输入框内容": "Will automatically read the input box content",
+ "移除模块的文档字符串": "Remove the module's docstrings",
+ "from crazy_functions.联网的ChatGPT_bing版 import 连接bing搜索回答问题": "from crazy_functions.online.ChatGPT_bing import connect_bing_search_to_answer_questions",
+ "关闭": "Close",
+ "学术论文": "Academic paper",
+ "多模态能力": "Multimodal capabilities",
+ "无渲染": "No rendering",
+ "弃用功能": "Deprecated feature",
+ "输入Searxng的地址": "Enter the address of Searxng",
+ "风格": "Style",
+ "介绍下第2点": "Introduce the second point",
+ "你的任务是结合历史记录": "Your task is to combine historical records",
+ "前端": "Frontend",
+ "采取措施丢弃一部分文本": "Take measures to discard some text",
+ "2. 输入包含图像": "2. Input contains images",
+ "输入问题": "Input question",
+ "可能原因": "Possible reasons",
+ "2. Java 是一种面向对象的编程语言": "Java is an object-oriented programming language",
+ "不支持的检索类型": "Unsupported retrieval type",
+ "第四步": "Step four",
+ "2. 机器学习在自然语言处理中的应用": "Applications of machine learning in natural language processing",
+ "浮动菜单定义": "Definition of floating menu",
+ "鿿": "Undefined",
+ "history 历史上下文": "History context",
+ "1. Java 是一种编译型语言": "Java is a compiled language",
+ "请根据给定的若干条搜索结果回答问题": "Answer the question based on the given search results",
+ "当输入文本 + 历史文本超出最大限制时": "When the input text + historical text exceeds the maximum limit",
+ "限DALLE3": "Limited to DALLE3",
+ "原问题": "Original question",
+ "日志文件": "Log file",
+ "输入图片描述": "Input image description",
+ "示例使用": "Example usage",
+ "后续参数": "Subsequent parameters",
+ "请用一句话对下面的程序文件做一个整体概述": "Please give a brief overview of the program file below in one sentence",
+ "当前对话是关于深度学习的介绍和应用等": "The current conversation is about the introduction and applications of deep learning",
+ "点击这里输入「关键词」搜索插件": "Click here to enter 'keywords' search plugin",
+ "按用户划分": "Divided by user",
+ "将结果写回源文件": "Write the results back to the source file",
+ "使用前切换到GPT系列模型": "Switch to GPT series model before using",
+ "正在读取下一段代码片段": "Reading the next code snippet",
+ "第二个搜索结果": "Second search result",
+ "作为一个学术论文搜索助手": "As an academic paper search assistant",
+ "搜索": "Search",
+ "无法从searxng获取信息!请尝试更换搜索引擎": "Unable to retrieve information from searxng! Please try changing the search engine",
+ "* 清洗搜索结果": "Cleaning search results",
+ "或者压缩包": "Or compressed file",
+ "模型": "Model",
+ "切换布局": "Switch layout",
+ "生成当前浏览器窗口的uuid": "Generate the uuid of the current browser window",
+ "左上角工具栏定义": "Definition of the top-left toolbar",
+ "from crazy_functions.联网的ChatGPT import ConnectToNetworkToAnswerQuestions": "from crazy_functions.ConnectToNetworkToAnswerQuestions import ChatGPT",
+ "对最相关的三个搜索结果进行总结": "Summarize the top three most relevant search results",
+ "刷新失效": "Refresh invalid",
+ "将处理后的 AST 转换回源代码": "Convert the processed AST back to source code",
+ "/* 插件下拉菜单 */": "/* Plugin dropdown menu */",
+ "移除类的文档字符串": "Remove the documentation strings of a class",
+ "请尽量不要修改": "Please try not to modify",
+ "并更换新的 API 秘钥": "And replace with a new API key",
+ "输入文件的路径": "Input file path",
+ "发现异常嵌套公式": "Identify nested formula exceptions",
+ "修复不标准的dollar公式符号的问题": "Fix the issue of non-standard dollar formula symbols",
+ "Searxng互联网检索服务": "Searxng Internet search service",
+ "联网检索中": "In network retrieval",
+ "并与“原问题语言相同”": "And in the same language as the original question",
+ "存在": "Exists",
+ "列出Java的三种特点": "List three characteristics of Java",
+ "3. Java 是一种跨平台的编程语言": "3. Java is a cross-platform programming language",
+ "所有源文件均已处理完毕": "All source files have been processed",
+ "限DALLE2": "Limited to DALLE2",
+ "紫东太初大模型 https": "Zidong Taichu Large Model https",
+ "🎨图片生成": "🎨 Image generation",
+ "1. 模型本身是多模态模型": "1. The model itself is multimodal",
+ "相关的信息": "Related information",
+ "* 或者使用搜索优化器": "* Or use a search optimizer",
+ "搜索查询": "Search query",
+ "当前对话是关于 Nginx 的介绍和在Ubuntu上的使用等": "The current conversation is about the introduction of Nginx and its use on Ubuntu, etc.",
+ "必须以json形式给出": "Must be provided in JSON format",
+ "开启": "Turn on",
+ "1. 机器学习在图像识别中的应用": "1. The application of machine learning in image recognition",
+ "处理代码片段": "Processing code snippet",
+ "则尝试获取其信息": "Then try to get its information",
+ "已完成的文件": "Completed file",
+ "注意这可能会消耗较多token": "Note that this may consume more tokens",
+ "多模型对话": "Multi-model conversation",
+ "现在有历史记录": "Now there is a history record",
+ "你知道 Python 么": "Do you know Python?",
+ "Base64编码": "Base64 encoding",
+ "Gradio的inbrowser触发不太稳定": "Gradio's in-browser trigger is not very stable",
+ "CJK标点符号": "CJK punctuation marks",
+ "请联系 Doc2x 方面": "Please contact Doc2x for details",
+ "耗尽generator避免报错": "Exhaust the generator to avoid errors",
+ "📚本地Latex论文精细翻译": "📚 Local Latex paper finely translated",
+ "* 尝试解码优化后的搜索结果": "* Try to decode the optimized search results",
+ "为这些代码添加docstring | 输入参数为路径": "Add docstring for these codes | Input parameter is path",
+ "读取插件参数": "Read plugin parameters",
+ "如果剩余的行数非常少": "If the remaining lines are very few",
+ "输出格式为JSON": "Output format is JSON",
+ "提取QaBox信息": "Extract QaBox information",
+ "不使用多模态能力": "Not using multimodal capabilities",
+ "解析源代码为 AST": "Parse source code into AST",
+ "使用前请切换模型到GPT系列": "Switch the model to GPT series before using",
+ "中文字符": "Chinese characters",
+ "用户的上传目录": "User's upload directory",
+ "请将文件上传后再执行该任务": "Please upload the file before executing this task",
+ "移除函数的文档字符串": "Remove the function's docstring",
+ "新版-更流畅": "New version - smoother",
+ "检索词": "Search term",
+ "获取插件参数": "Get plugin parameters",
+ "获取插件执行函数": "Get plugin execution function",
+ "为“原问题”生成个不同版本的“检索词”": "Generate different versions of 'search terms' for the 'original question'",
+ "并清洗重复的搜索结果": "Clean and remove duplicate search results",
+ "直接返回原始问题": "Directly return the original question",
+ "从不同角度": "From different perspectives",
+ "展示已经完成的部分": "Display completed parts",
+ "搜索优化": "Search optimization",
+ "解决缩进问题": "Resolve indentation issues",
+ "直接给出最多{num}个检索词": "Directly provide up to {num} search terms",
+ "对话数据": "Conversation data",
+ "定义一个正则表达式来匹配 Base64 字符串": "Define a regular expression to match Base64 strings",
+ "转化为kwargs字典": "Convert to kwargs dictionary",
+ "原始数据": "Original data",
+ "当以下条件满足时": "When the following conditions are met",
+ "主题修改": "Topic modification",
+ "Searxng服务地址": "Searxng service address",
+ "3. 机器学习在推荐系统中的应用": "3. Application of machine learning in recommendation systems",
+ "全角符号": "Full-width symbols",
+ "发送到大模型进行分析": "Send for analysis to a large model",
+ "一个常用的测试目录": "A commonly used test directory",
+ "在线搜索失败!": "Online search failed!",
+ "搜索语言": "Search language",
+ "万事俱备": "All is ready",
+ "指定了后续参数的名称": "Specified the names of subsequent parameters",
+ "是否使用搜索增强": "Whether to use search enhancement",
+ "你知道 GAN 么": "Do you know about GAN?",
+ "├── 互联网检索": "├── Internet retrieval",
+ "公式之中出现了异常": "An anomaly occurred in the formula",
+ "当前对话是关于深度学习的介绍和在图像识别中的应用等": "The current conversation is about the introduction of deep learning and its applications in image recognition, etc.",
+ "返回反转后的 Base64 字符串列表": "Return a list of Base64 strings reversed",
+ "一鼓作气处理掉": "Deal with it in one go",
+ "剩余源文件数量": "Remaining source file count",
+ "查互联网后回答": "Answer after checking the internet",
+ "需要生成图像的文本描述": "Text description for generating images",
+ "* 如果再次失败": "If failed again",
+ "质量": "Quality",
+ "请配置 TAICHU_API_KEY": "Please configure TAICHU_API_KEY",
+ "most_recent_uploaded 是一个放置最新上传图像的路径": "most_recent_uploaded is a path to place the latest uploaded images",
+ "真正的参数": "Actual parameters",
+ "生成带注释文件": "Generate files with annotations",
+ "源自": "From",
+ "怎么下载": "How to download",
+ "请稍后": "Please wait",
+ "会尝试结合历史记录进行搜索优化": "Will try to optimize the search by combining historical records",
+ "max_token_limit 最大token限制": "max_token_limit maximum token limit"
}
\ No newline at end of file
diff --git a/docs/translate_std.json b/docs/translate_std.json
index 0b3faf428..50889da79 100644
--- a/docs/translate_std.json
+++ b/docs/translate_std.json
@@ -106,5 +106,7 @@
"解析PDF_DOC2X_转Latex": "ParsePDF_DOC2X_toLatex",
"解析PDF_基于DOC2X": "ParsePDF_basedDOC2X",
"解析PDF_简单拆解": "ParsePDF_simpleDecomposition",
- "解析PDF_DOC2X_单文件": "ParsePDF_DOC2X_singleFile"
+ "解析PDF_DOC2X_单文件": "ParsePDF_DOC2X_singleFile",
+ "注释Python项目": "CommentPythonProject",
+ "注释源代码": "CommentSourceCode"
}
\ No newline at end of file
diff --git a/main.py b/main.py
index 8de1cc2cc..294f64aff 100644
--- a/main.py
+++ b/main.py
@@ -118,8 +118,8 @@ def main():
choices=[
"常规对话",
"多模型对话",
+ "智能召回 RAG",
# "智能上下文",
- # "智能召回 RAG",
], value="常规对话",
interactive=True, label='', show_label=False,
elem_classes='normal_mut_select', elem_id="gpt-submit-dropdown").style(container=False)
diff --git a/request_llms/bridge_all.py b/request_llms/bridge_all.py
index 931a899b5..d52c4d7fc 100644
--- a/request_llms/bridge_all.py
+++ b/request_llms/bridge_all.py
@@ -349,6 +349,23 @@ def decode(self, *args, **kwargs):
"token_cnt": get_token_num_gpt4,
},
+ "o1-preview": {
+ "fn_with_ui": chatgpt_ui,
+ "fn_without_ui": chatgpt_noui,
+ "endpoint": openai_endpoint,
+ "max_token": 128000,
+ "tokenizer": tokenizer_gpt4,
+ "token_cnt": get_token_num_gpt4,
+ },
+ "o1-mini": {
+ "fn_with_ui": chatgpt_ui,
+ "fn_without_ui": chatgpt_noui,
+ "endpoint": openai_endpoint,
+ "max_token": 128000,
+ "tokenizer": tokenizer_gpt4,
+ "token_cnt": get_token_num_gpt4,
+ },
+
"gpt-4-turbo": {
"fn_with_ui": chatgpt_ui,
"fn_without_ui": chatgpt_noui,
@@ -508,22 +525,46 @@ def decode(self, *args, **kwargs):
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
+ # Gemini
+ # Note: now gemini-pro is an alias of gemini-1.0-pro.
+ # Warning: gemini-pro-vision has been deprecated.
+ # Support for gemini-pro-vision has been removed.
"gemini-pro": {
"fn_with_ui": genai_ui,
"fn_without_ui": genai_noui,
"endpoint": gemini_endpoint,
+ "has_multimodal_capacity": False,
"max_token": 1024 * 32,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
- "gemini-pro-vision": {
+ "gemini-1.0-pro": {
"fn_with_ui": genai_ui,
"fn_without_ui": genai_noui,
"endpoint": gemini_endpoint,
+ "has_multimodal_capacity": False,
"max_token": 1024 * 32,
"tokenizer": tokenizer_gpt35,
"token_cnt": get_token_num_gpt35,
},
+ "gemini-1.5-pro": {
+ "fn_with_ui": genai_ui,
+ "fn_without_ui": genai_noui,
+ "endpoint": gemini_endpoint,
+ "has_multimodal_capacity": True,
+ "max_token": 1024 * 204800,
+ "tokenizer": tokenizer_gpt35,
+ "token_cnt": get_token_num_gpt35,
+ },
+ "gemini-1.5-flash": {
+ "fn_with_ui": genai_ui,
+ "fn_without_ui": genai_noui,
+ "endpoint": gemini_endpoint,
+ "has_multimodal_capacity": True,
+ "max_token": 1024 * 204800,
+ "tokenizer": tokenizer_gpt35,
+ "token_cnt": get_token_num_gpt35,
+ },
# cohere
"cohere-command-r-plus": {
@@ -958,7 +999,7 @@ def decode(self, *args, **kwargs):
})
except:
print(trimmed_format_exc())
-if "sparkv3" in AVAIL_LLM_MODELS or "sparkv3.5" in AVAIL_LLM_MODELS: # 讯飞星火认知大模型
+if any(x in AVAIL_LLM_MODELS for x in ("sparkv3", "sparkv3.5", "sparkv4")): # 讯飞星火认知大模型
try:
from .bridge_spark import predict_no_ui_long_connection as spark_noui
from .bridge_spark import predict as spark_ui
diff --git a/request_llms/bridge_chatglm3.py b/request_llms/bridge_chatglm3.py
index d79067b67..95b629d19 100644
--- a/request_llms/bridge_chatglm3.py
+++ b/request_llms/bridge_chatglm3.py
@@ -18,7 +18,7 @@ def load_model_info(self):
def load_model_and_tokenizer(self):
# 🏃♂️🏃♂️🏃♂️ 子进程执行
- from transformers import AutoModel, AutoTokenizer
+ from transformers import AutoModel, AutoTokenizer, BitsAndBytesConfig
import os, glob
import os
import platform
@@ -45,15 +45,13 @@ def load_model_and_tokenizer(self):
chatglm_model = AutoModel.from_pretrained(
pretrained_model_name_or_path=_model_name_,
trust_remote_code=True,
- device="cuda",
- load_in_4bit=True,
+ quantization_config=BitsAndBytesConfig(load_in_4bit=True),
)
elif LOCAL_MODEL_QUANT == "INT8": # INT8
chatglm_model = AutoModel.from_pretrained(
pretrained_model_name_or_path=_model_name_,
trust_remote_code=True,
- device="cuda",
- load_in_8bit=True,
+ quantization_config=BitsAndBytesConfig(load_in_8bit=True),
)
else:
chatglm_model = AutoModel.from_pretrained(
diff --git a/request_llms/bridge_google_gemini.py b/request_llms/bridge_google_gemini.py
index 9bf4e23a6..3697b8fcc 100644
--- a/request_llms/bridge_google_gemini.py
+++ b/request_llms/bridge_google_gemini.py
@@ -8,15 +8,15 @@
import time
from request_llms.com_google import GoogleChatInit
from toolbox import ChatBotWithCookies
-from toolbox import get_conf, update_ui, update_ui_lastest_msg, have_any_recent_upload_image_files, trimmed_format_exc, log_chat
+from toolbox import get_conf, update_ui, update_ui_lastest_msg, have_any_recent_upload_image_files, trimmed_format_exc, log_chat, encode_image
proxies, TIMEOUT_SECONDS, MAX_RETRY = get_conf('proxies', 'TIMEOUT_SECONDS', 'MAX_RETRY')
timeout_bot_msg = '[Local Message] Request timeout. Network error. Please check proxy settings in config.py.' + \
'网络错误,检查代理服务器是否可用,以及代理设置的格式是否正确,格式须是[协议]://[地址]:[端口],缺一不可。'
-def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="", observe_window=None,
- console_slience=False):
+def predict_no_ui_long_connection(inputs:str, llm_kwargs:dict, history:list=[], sys_prompt:str="", observe_window:list=[],
+ console_slience:bool=False):
# 检查API_KEY
if get_conf("GEMINI_API_KEY") == "":
raise ValueError(f"请配置 GEMINI_API_KEY。")
@@ -44,9 +44,20 @@ def predict_no_ui_long_connection(inputs, llm_kwargs, history=[], sys_prompt="",
raise RuntimeError(f'{gpt_replying_buffer} 对话错误')
return gpt_replying_buffer
+def make_media_input(inputs, image_paths):
+ image_base64_array = []
+ for image_path in image_paths:
+ path = os.path.abspath(image_path)
+ inputs = inputs + f'