Files
CavinHuang-comfyui-nodes-docs/docs/LLMChatBot.md
2024-07-04 08:39:03 +08:00

190 lines
7.1 KiB
Markdown

# Documentation
- Class name: LLMChatBot
- Category: SALT/Language Toolkit/Querying
- Output node: False
LLMChatBot节点旨在通过利用大型语言模型(LLMs)实现交互式聊天对话。它结合用户输入、上下文信息和预设参数来生成对话回应,以模拟自然而生动的对话体验。
# Input types
## Required
- llm_model
- 指定用于聊天对话的大型语言模型,包括相关配置和设置。这个参数决定了聊天机器人的基础能力和表现。
- Comfy dtype: LLM_MODEL
- Python dtype: Dict[str, Any]
- llm_context
- 提供特定于聊天会话的额外上下文或设置,增强模型的理解能力和回应准确性。这有助于定制化聊天体验。
- Comfy dtype: LLM_CONTEXT
- Python dtype: Any
- prompt
- 用户的输入消息或问题,作为触发聊天机器人生成回复的关键。这是每轮对话的核心输入。
- Comfy dtype: STRING
- Python dtype: str
## Optional
- reset_engine
- 重置聊天引擎的标志,用于开始新的对话线程或清除历史记录。这对于管理长期对话或切换话题很有用。
- Comfy dtype: BOOLEAN
- Python dtype: bool
- user_nickname
- 用户的自定义昵称,用于个性化聊天会话。这增加了对话的亲和力。
- Comfy dtype: STRING
- Python dtype: str
- system_nickname
- 聊天机器人的自定义昵称,通过添加个性化层面来增强对话体验。这有助于塑造机器人的独特性格。
- Comfy dtype: STRING
- Python dtype: str
- char_per_token
- 定义每个令牌的平均字符数,用于聊天会话中的分词目的。这影响了模型对输入文本的处理方式。
- Comfy dtype: INT
- Python dtype: int
# Output types
- chat_history
- 用户和聊天机器人之间累积的对话历史,包括所有交换的消息。这提供了完整的对话上下文。
- Comfy dtype: STRING
- Python dtype: List[Dict[str, Any]]
- response
- 聊天机器人对用户最新提示的直接回应。这是每轮对话中机器人生成的核心输出。
- Comfy dtype: STRING
- Python dtype: str
- chat_token_count
- 聊天会话中使用的令牌总数,反映了对话的复杂性和长度。这有助于监控和管理对话的规模。
- Comfy dtype: INT
- Python dtype: int
## Usage tips
- Infra type: `CPU`
- Common nodes: unknown
## Source code
```python
class LLMChatBot:
def __init__(self):
self.chat_history = []
self.history = []
self.token_map = {}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"llm_model": ("LLM_MODEL", ),
"llm_context": ("LLM_CONTEXT", ),
"prompt": ("STRING", {"multiline": True, "dynamicPrompt": False}),
},
"optional": {
"reset_engine": ("BOOLEAN", {"default": False}),
"user_nickname": ("STRING", {"default": "User"}),
"system_nickname": ("STRING", {"default": "Assistant"}),
"char_per_token": ("INT", {"min": 1, "default": 4})
}
}
RETURN_TYPES = ("STRING", "STRING", "INT")
RETURN_NAMES = ("chat_history", "response", "chat_token_count")
FUNCTION = "chat"
CATEGORY = f"{MENU_NAME}/{SUB_MENU_NAME}/Querying"
def chat(self, llm_model: Dict[str, Any], llm_context:Any, prompt: str, reset_engine:bool = False, user_nickname:str = "User", system_nickname:str = "Assistant", char_per_token:int = 4) -> str:
if reset_engine:
self.chat_history.clear()
self.history.clear()
self.token_map.clear()
max_tokens = llm_model.get("max_tokens", 4096)
using_mock_tokenizer = False
try:
tokenizer = tiktoken.encoding_for_model(llm_model.get('llm_name', 'gpt-3-turbo'))
except Exception:
using_mock_tokenizer = True
tokenizer = MockTokenizer(max_tokens, char_per_token=char_per_token)
if not self.chat_history:
system_prompt = getattr(llm_model['llm'], "system_prompt", None)
if system_prompt not in (None, ""):
initial_msg = ChatMessage(role=MessageRole.SYSTEM, content=system_prompt)
self.chat_history.append(initial_msg)
self.token_map[0] = tokenizer.encode(system_prompt)
# Tokenize and count initial tokens
cumulative_token_count = 0
for index, message in enumerate(self.chat_history):
if index not in self.token_map:
self.token_map[index] = tokenizer.encode(message.content)
#if not using_mock_tokenizer:
cumulative_token_count += len(self.token_map[index])
#else:
# cumulative_token_count += tokenizer.count(self.token_map[index])
# Prune messages from the history if over max_tokens
index = 0
while cumulative_token_count > max_tokens and index < len(self.chat_history):
tokens = self.token_map[index]
token_count = len(tokens) #if not using_mock_tokenizer else tokenizer.count(tokens)
if token_count > 1:
tokens.pop(0)
self.chat_history[index].content = tokenizer.decode(tokens)
cumulative_token_count -= 1
else:
cumulative_token_count -= token_count
self.chat_history.pop(index)
self.token_map.pop(index)
for old_index in list(self.token_map.keys()):
if old_index > index:
self.token_map[old_index - 1] = self.token_map.pop(old_index)
continue
index += 1
history_string = ""
reply_string = ""
documents = []
# Build prior history string
for history in self.history:
user, assistant, timestamp = history
history_string += f"""[{user_nickname}]: {history[user]}
[{system_nickname}]: {history[assistant]}
"""
# Spoof documents -- Why can't we just talk to a modeL?
documents = [Document(text="null", extra_info={})]
index = VectorStoreIndex.from_documents(
documents,
service_context=llm_context,
transformations=[SentenceSplitter(chunk_size=1024, chunk_overlap=20)]
)
chat_engine = index.as_chat_engine(chat_mode="best")
response = chat_engine.chat(prompt, chat_history=self.chat_history)
response_dict = {
user_nickname: prompt,
system_nickname: response.response,
"timestamp": str(time.time())
}
user_cm = ChatMessage(role=MessageRole.USER, content=prompt)
system_cm = ChatMessage(role=MessageRole.SYSTEM, content=response.response)
self.chat_history.append(user_cm)
self.chat_history.append(system_cm)
self.history.append(response_dict)
reply_string = response.response
history_string += f"""[{user_nickname}]: {prompt}
[{system_nickname}]: {response.response}"""
pprint(self.chat_history, indent=4)
return (history_string, reply_string, cumulative_token_count)
```