mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-05 10:05:05 +08:00
Compare commits
8 Commits
9db999ccae
...
1deb0a2d42
| Author | SHA1 | Date | |
|---|---|---|---|
| 1deb0a2d42 | |||
| dd055deee9 | |||
| a249803961 | |||
| 6ec3f18e22 | |||
| 7724acbadb | |||
| a36ba95c1c | |||
| 30ccc4a66c | |||
| dda5a0080a |
@ -20,94 +20,128 @@ BEGIN_SEARCH_RESULT = "<|begin_search_result|>"
|
|||||||
END_SEARCH_RESULT = "<|end_search_result|>"
|
END_SEARCH_RESULT = "<|end_search_result|>"
|
||||||
MAX_SEARCH_LIMIT = 6
|
MAX_SEARCH_LIMIT = 6
|
||||||
|
|
||||||
REASON_PROMPT = (
|
REASON_PROMPT = f"""You are an advanced reasoning agent. Your goal is to answer the user's question by breaking it down into a series of verifiable steps.
|
||||||
"You are a reasoning assistant with the ability to perform dataset searches to help "
|
|
||||||
"you answer the user's question accurately. You have special tools:\n\n"
|
|
||||||
f"- To perform a search: write {BEGIN_SEARCH_QUERY} your query here {END_SEARCH_QUERY}.\n"
|
|
||||||
f"Then, the system will search and analyze relevant content, then provide you with helpful information in the format {BEGIN_SEARCH_RESULT} ...search results... {END_SEARCH_RESULT}.\n\n"
|
|
||||||
f"You can repeat the search process multiple times if necessary. The maximum number of search attempts is limited to {MAX_SEARCH_LIMIT}.\n\n"
|
|
||||||
"Once you have all the information you need, continue your reasoning.\n\n"
|
|
||||||
"-- Example 1 --\n" ########################################
|
|
||||||
"Question: \"Are both the directors of Jaws and Casino Royale from the same country?\"\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY}Who is the director of Jaws?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nThe director of Jaws is Steven Spielberg...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information.\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY}Where is Steven Spielberg from?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nSteven Allan Spielberg is an American filmmaker...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information...\n\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY}Who is the director of Casino Royale?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nCasino Royale is a 2006 spy film directed by Martin Campbell...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information...\n\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY}Where is Martin Campbell from?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nMartin Campbell (born 24 October 1943) is a New Zealand film and television director...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information...\n\n"
|
|
||||||
"Assistant:\nIt's enough to answer the question\n"
|
|
||||||
|
|
||||||
"-- Example 2 --\n" #########################################
|
You have access to a powerful search tool to find information.
|
||||||
"Question: \"When was the founder of craigslist born?\"\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY}Who was the founder of craigslist?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nCraigslist was founded by Craig Newmark...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information.\n"
|
|
||||||
"Assistant:\n"
|
|
||||||
f" {BEGIN_SEARCH_QUERY} When was Craig Newmark born?{END_SEARCH_QUERY}\n\n"
|
|
||||||
"User:\n"
|
|
||||||
f" {BEGIN_SEARCH_RESULT}\nCraig Newmark was born on December 6, 1952...\n{END_SEARCH_RESULT}\n\n"
|
|
||||||
"Continues reasoning with the new information...\n\n"
|
|
||||||
"Assistant:\nIt's enough to answer the question\n"
|
|
||||||
"**Remember**:\n"
|
|
||||||
f"- You have a dataset to search, so you just provide a proper search query.\n"
|
|
||||||
f"- Use {BEGIN_SEARCH_QUERY} to request a dataset search and end with {END_SEARCH_QUERY}.\n"
|
|
||||||
"- The language of query MUST be as the same as 'Question' or 'search result'.\n"
|
|
||||||
"- If no helpful information can be found, rewrite the search query to be less and precise keywords.\n"
|
|
||||||
"- When done searching, continue your reasoning.\n\n"
|
|
||||||
'Please answer the following question. You should think step by step to solve it.\n\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
RELEVANT_EXTRACTION_PROMPT = """**Task Instruction:**
|
**Your Task:**
|
||||||
|
1. Analyze the user's question.
|
||||||
|
2. If you need information, issue a search query to find a specific fact.
|
||||||
|
3. Review the search results.
|
||||||
|
4. Repeat the search process until you have all the facts needed to answer the question.
|
||||||
|
5. Once you have gathered sufficient information, synthesize the facts and provide the final answer directly.
|
||||||
|
|
||||||
You are tasked with reading and analyzing web pages based on the following inputs: **Previous Reasoning Steps**, **Current Search Query**, and **Searched Web Pages**. Your objective is to extract relevant and helpful information for **Current Search Query** from the **Searched Web Pages** and seamlessly integrate this information into the **Previous Reasoning Steps** to continue reasoning for the original question.
|
**Tool Usage:**
|
||||||
|
- To search, you MUST write your query between the special tokens: {BEGIN_SEARCH_QUERY}your query{END_SEARCH_QUERY}.
|
||||||
|
- The system will provide results between {BEGIN_SEARCH_RESULT}search results{END_SEARCH_RESULT}.
|
||||||
|
- You have a maximum of {MAX_SEARCH_LIMIT} search attempts.
|
||||||
|
|
||||||
**Guidelines:**
|
---
|
||||||
|
**Example 1: Multi-hop Question**
|
||||||
|
|
||||||
1. **Analyze the Searched Web Pages:**
|
**Question:** "Are both the directors of Jaws and Casino Royale from the same country?"
|
||||||
- Carefully review the content of each searched web page.
|
|
||||||
- Identify factual information that is relevant to the **Current Search Query** and can aid in the reasoning process for the original question.
|
|
||||||
|
|
||||||
2. **Extract Relevant Information:**
|
**Your Thought Process & Actions:**
|
||||||
- Select the information from the Searched Web Pages that directly contributes to advancing the **Previous Reasoning Steps**.
|
First, I need to identify the director of Jaws.
|
||||||
- Ensure that the extracted information is accurate and relevant.
|
{BEGIN_SEARCH_QUERY}who is the director of Jaws?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Jaws is a 1975 American thriller film directed by Steven Spielberg.
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
Okay, the director of Jaws is Steven Spielberg. Now I need to find out his nationality.
|
||||||
|
{BEGIN_SEARCH_QUERY}where is Steven Spielberg from?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Steven Allan Spielberg is an American filmmaker. Born in Cincinnati, Ohio...
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
So, Steven Spielberg is from the USA. Next, I need to find the director of Casino Royale.
|
||||||
|
{BEGIN_SEARCH_QUERY}who is the director of Casino Royale 2006?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Casino Royale is a 2006 spy film directed by Martin Campbell.
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
The director of Casino Royale is Martin Campbell. Now I need his nationality.
|
||||||
|
{BEGIN_SEARCH_QUERY}where is Martin Campbell from?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Martin Campbell (born 24 October 1943) is a New Zealand film and television director.
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
I have all the information. Steven Spielberg is from the USA, and Martin Campbell is from New Zealand. They are not from the same country.
|
||||||
|
|
||||||
3. **Output Format:**
|
Final Answer: No, the directors of Jaws and Casino Royale are not from the same country. Steven Spielberg is from the USA, and Martin Campbell is from New Zealand.
|
||||||
- **If the web pages provide helpful information for current search query:** Present the information beginning with `**Final Information**` as shown below.
|
|
||||||
- The language of query **MUST BE** as the same as 'Search Query' or 'Web Pages'.\n"
|
|
||||||
**Final Information**
|
|
||||||
|
|
||||||
[Helpful information]
|
---
|
||||||
|
**Example 2: Simple Fact Retrieval**
|
||||||
|
|
||||||
- **If the web pages do not provide any helpful information for current search query:** Output the following text.
|
**Question:** "When was the founder of craigslist born?"
|
||||||
|
|
||||||
**Final Information**
|
**Your Thought Process & Actions:**
|
||||||
|
First, I need to know who founded craigslist.
|
||||||
|
{BEGIN_SEARCH_QUERY}who founded craigslist?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Craigslist was founded in 1995 by Craig Newmark.
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
The founder is Craig Newmark. Now I need his birth date.
|
||||||
|
{BEGIN_SEARCH_QUERY}when was Craig Newmark born?{END_SEARCH_QUERY}
|
||||||
|
[System returns search results]
|
||||||
|
{BEGIN_SEARCH_RESULT}
|
||||||
|
Craig Newmark was born on December 6, 1952.
|
||||||
|
{END_SEARCH_RESULT}
|
||||||
|
I have found the answer.
|
||||||
|
|
||||||
No helpful information found.
|
Final Answer: The founder of craigslist, Craig Newmark, was born on December 6, 1952.
|
||||||
|
|
||||||
**Inputs:**
|
---
|
||||||
- **Previous Reasoning Steps:**
|
**Important Rules:**
|
||||||
{prev_reasoning}
|
- **One Fact at a Time:** Decompose the problem and issue one search query at a time to find a single, specific piece of information.
|
||||||
|
- **Be Precise:** Formulate clear and precise search queries. If a search fails, rephrase it.
|
||||||
|
- **Synthesize at the End:** Do not provide the final answer until you have completed all necessary searches.
|
||||||
|
- **Language Consistency:** Your search queries should be in the same language as the user's question.
|
||||||
|
|
||||||
- **Current Search Query:**
|
Now, begin your work. Please answer the following question by thinking step-by-step.
|
||||||
{search_query}
|
"""
|
||||||
|
|
||||||
- **Searched Web Pages:**
|
RELEVANT_EXTRACTION_PROMPT = """You are a highly efficient information extraction module. Your sole purpose is to extract the single most relevant piece of information from the provided `Searched Web Pages` that directly answers the `Current Search Query`.
|
||||||
{document}
|
|
||||||
|
|
||||||
"""
|
**Your Task:**
|
||||||
|
1. Read the `Current Search Query` to understand what specific information is needed.
|
||||||
|
2. Scan the `Searched Web Pages` to find the answer to that query.
|
||||||
|
3. Extract only the essential, factual information that answers the query. Be concise.
|
||||||
|
|
||||||
|
**Context (For Your Information Only):**
|
||||||
|
The `Previous Reasoning Steps` are provided to give you context on the overall goal, but your primary focus MUST be on answering the `Current Search Query`. Do not use information from the previous steps in your output.
|
||||||
|
|
||||||
|
**Output Format:**
|
||||||
|
Your response must follow one of two formats precisely.
|
||||||
|
|
||||||
|
1. **If a direct and relevant answer is found:**
|
||||||
|
- Start your response immediately with `Final Information`.
|
||||||
|
- Provide only the extracted fact(s). Do not add any extra conversational text.
|
||||||
|
|
||||||
|
*Example:*
|
||||||
|
`Current Search Query`: Where is Martin Campbell from?
|
||||||
|
`Searched Web Pages`: [Long article snippet about Martin Campbell's career, which includes the sentence "Martin Campbell (born 24 October 1943) is a New Zealand film and television director..."]
|
||||||
|
|
||||||
|
*Your Output:*
|
||||||
|
Final Information
|
||||||
|
Martin Campbell is a New Zealand film and television director.
|
||||||
|
|
||||||
|
2. **If no relevant answer that directly addresses the query is found in the web pages:**
|
||||||
|
- Start your response immediately with `Final Information`.
|
||||||
|
- Write the exact phrase: `No helpful information found.`
|
||||||
|
|
||||||
|
---
|
||||||
|
**BEGIN TASK**
|
||||||
|
|
||||||
|
**Inputs:**
|
||||||
|
|
||||||
|
- **Previous Reasoning Steps:**
|
||||||
|
{prev_reasoning}
|
||||||
|
|
||||||
|
- **Current Search Query:**
|
||||||
|
{search_query}
|
||||||
|
|
||||||
|
- **Searched Web Pages:**
|
||||||
|
{document}
|
||||||
|
"""
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -75,11 +75,13 @@ def retrieval(tenant_id):
|
|||||||
for c in ranks["chunks"]:
|
for c in ranks["chunks"]:
|
||||||
e, doc = DocumentService.get_by_id( c["doc_id"])
|
e, doc = DocumentService.get_by_id( c["doc_id"])
|
||||||
c.pop("vector", None)
|
c.pop("vector", None)
|
||||||
|
meta = getattr(doc, 'meta_fields', {})
|
||||||
|
meta["doc_id"] = c["doc_id"]
|
||||||
records.append({
|
records.append({
|
||||||
"content": c["content_with_weight"],
|
"content": c["content_with_weight"],
|
||||||
"score": c["similarity"],
|
"score": c["similarity"],
|
||||||
"title": c["docnm_kwd"],
|
"title": c["docnm_kwd"],
|
||||||
"metadata": getattr(doc, 'meta_fields', {})
|
"metadata": meta
|
||||||
})
|
})
|
||||||
|
|
||||||
return jsonify({"records": records})
|
return jsonify({"records": records})
|
||||||
|
|||||||
@ -173,7 +173,8 @@ def completion(tenant_id, agent_id, session_id=None, **kwargs):
|
|||||||
conv.message.append({"role": "assistant", "content": txt, "created_at": time.time(), "id": message_id})
|
conv.message.append({"role": "assistant", "content": txt, "created_at": time.time(), "id": message_id})
|
||||||
conv.reference = canvas.get_reference()
|
conv.reference = canvas.get_reference()
|
||||||
conv.errors = canvas.error
|
conv.errors = canvas.error
|
||||||
API4ConversationService.append_message(conv.id, conv.to_dict())
|
conv = conv.to_dict()
|
||||||
|
API4ConversationService.append_message(conv["id"], conv)
|
||||||
|
|
||||||
|
|
||||||
def completionOpenAI(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
|
def completionOpenAI(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
|
||||||
|
|||||||
@ -225,6 +225,9 @@ class TenantLLMService(CommonService):
|
|||||||
if llm_id == llm["llm_name"]:
|
if llm_id == llm["llm_name"]:
|
||||||
return llm["model_type"].split(",")[-1]
|
return llm["model_type"].split(",")[-1]
|
||||||
|
|
||||||
|
for llm in LLMService.query(llm_name=llm_id):
|
||||||
|
return llm.model_type
|
||||||
|
|
||||||
|
|
||||||
class LLMBundle:
|
class LLMBundle:
|
||||||
def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
|
def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
|
||||||
|
|||||||
@ -82,7 +82,7 @@ An integer specifying the number of previous dialogue rounds to input into the L
|
|||||||
This feature is used for multi-turn dialogue *only*.
|
This feature is used for multi-turn dialogue *only*.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Max retrieves
|
### Max retries
|
||||||
|
|
||||||
Defines the maximum number of attempts the agent will make to retry a failed task or operation before stopping or reporting failure.
|
Defines the maximum number of attempts the agent will make to retry a failed task or operation before stopping or reporting failure.
|
||||||
|
|
||||||
@ -94,6 +94,10 @@ The waiting period in seconds that the agent observes before retrying a failed t
|
|||||||
|
|
||||||
Defines the maximum number reflection rounds of the selected chat model. Defaults to 5 rounds.
|
Defines the maximum number reflection rounds of the selected chat model. Defaults to 5 rounds.
|
||||||
|
|
||||||
|
:::tip NOTE
|
||||||
|
You can set the value to 1 to shorten your agent's response time.
|
||||||
|
:::
|
||||||
|
|
||||||
### Output
|
### Output
|
||||||
|
|
||||||
The global variable name for the output of the **Agent** component, which can be referenced by other components in the workflow.
|
The global variable name for the output of the **Agent** component, which can be referenced by other components in the workflow.
|
||||||
@ -34,7 +34,7 @@ From v0.20.0 onwards, Agents are no longer compatible with earlier versions, and
|
|||||||
|
|
||||||
- Unified orchestration of both Agents and Workflows.
|
- Unified orchestration of both Agents and Workflows.
|
||||||
- A comprehensive refactor of the Agent, greatly enhancing its capabilities and usability, with support for Multi-Agent configurations, planning and reflection, and visual functionalities.
|
- A comprehensive refactor of the Agent, greatly enhancing its capabilities and usability, with support for Multi-Agent configurations, planning and reflection, and visual functionalities.
|
||||||
- Fully realized MCP functionality, allowing for MCP Server import, Agents functioning as MCP Clients, and RAGFlow itself operating as an MCP Server.
|
- Fully implemented MCP functionality, allowing for MCP Server import, Agents functioning as MCP Clients, and RAGFlow itself operating as an MCP Server.
|
||||||
- Access to runtime logs for Agents.
|
- Access to runtime logs for Agents.
|
||||||
- Chat histories with Agents available through the management panel.
|
- Chat histories with Agents available through the management panel.
|
||||||
- Integration of a new, more robust version of Infinity, enabling the auto-tagging functionality with Infinity as the underlying document engine.
|
- Integration of a new, more robust version of Infinity, enabling the auto-tagging functionality with Infinity as the underlying document engine.
|
||||||
@ -43,7 +43,7 @@ From v0.20.0 onwards, Agents are no longer compatible with earlier versions, and
|
|||||||
- RAGFlow’s codebase is now mirrored on Gitee.
|
- RAGFlow’s codebase is now mirrored on Gitee.
|
||||||
- Introduction of a new model provider, Gitee AI.
|
- Introduction of a new model provider, Gitee AI.
|
||||||
|
|
||||||
### New agent templates
|
### New agent templates introduced
|
||||||
|
|
||||||
- Multi-Agent based Deep Research: Collaborative Agent teamwork led by a Lead Agent with multiple Subagents, distinct from traditional workflow orchestration.
|
- Multi-Agent based Deep Research: Collaborative Agent teamwork led by a Lead Agent with multiple Subagents, distinct from traditional workflow orchestration.
|
||||||
- An intelligent Q&A chatbot leveraging internal knowledge bases, designed for customer service and training scenarios.
|
- An intelligent Q&A chatbot leveraging internal knowledge bases, designed for customer service and training scenarios.
|
||||||
|
|||||||
@ -59,6 +59,10 @@ class Base(ABC):
|
|||||||
def _image_prompt(self, text, images):
|
def _image_prompt(self, text, images):
|
||||||
if not images:
|
if not images:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
if isinstance(images, str):
|
||||||
|
images = [images]
|
||||||
|
|
||||||
pmpt = [{"type": "text", "text": text}]
|
pmpt = [{"type": "text", "text": text}]
|
||||||
for img in images:
|
for img in images:
|
||||||
pmpt.append({
|
pmpt.append({
|
||||||
@ -518,6 +522,7 @@ class GeminiCV(Base):
|
|||||||
def chat_streamly(self, system, history, gen_conf, images=[]):
|
def chat_streamly(self, system, history, gen_conf, images=[]):
|
||||||
from transformers import GenerationConfig
|
from transformers import GenerationConfig
|
||||||
ans = ""
|
ans = ""
|
||||||
|
response = None
|
||||||
try:
|
try:
|
||||||
response = self.model.generate_content(
|
response = self.model.generate_content(
|
||||||
self._form_history(system, history, images),
|
self._form_history(system, history, images),
|
||||||
@ -533,8 +538,10 @@ class GeminiCV(Base):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield ans + "\n**ERROR**: " + str(e)
|
yield ans + "\n**ERROR**: " + str(e)
|
||||||
|
|
||||||
yield response._chunks[-1].usage_metadata.total_token_count
|
if response and hasattr(response, "usage_metadata") and hasattr(response.usage_metadata, "total_token_count"):
|
||||||
|
yield response.usage_metadata.total_token_count
|
||||||
|
else:
|
||||||
|
yield 0
|
||||||
|
|
||||||
class NvidiaCV(Base):
|
class NvidiaCV(Base):
|
||||||
_FACTORY_NAME = "NVIDIA"
|
_FACTORY_NAME = "NVIDIA"
|
||||||
@ -795,4 +802,4 @@ class GoogleCV(AnthropicCV, GeminiCV):
|
|||||||
yield ans
|
yield ans
|
||||||
else:
|
else:
|
||||||
for ans in GeminiCV.chat_streamly(self, system, history, gen_conf, images):
|
for ans in GeminiCV.chat_streamly(self, system, history, gen_conf, images):
|
||||||
yield ans
|
yield ans
|
||||||
|
|||||||
@ -227,9 +227,20 @@ class RedisDB:
|
|||||||
"""https://redis.io/docs/latest/commands/xreadgroup/"""
|
"""https://redis.io/docs/latest/commands/xreadgroup/"""
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
try:
|
try:
|
||||||
group_info = self.REDIS.xinfo_groups(queue_name)
|
|
||||||
if not any(gi["name"] == group_name for gi in group_info):
|
try:
|
||||||
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
group_info = self.REDIS.xinfo_groups(queue_name)
|
||||||
|
if not any(gi["name"] == group_name for gi in group_info):
|
||||||
|
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
||||||
|
except redis.exceptions.ResponseError as e:
|
||||||
|
if "no such key" in str(e).lower():
|
||||||
|
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
||||||
|
elif "busygroup" in str(e).lower():
|
||||||
|
logging.warning("Group already exists, continue.")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
"groupname": group_name,
|
"groupname": group_name,
|
||||||
"consumername": consumer_name,
|
"consumername": consumer_name,
|
||||||
@ -338,8 +349,8 @@ class RedisDB:
|
|||||||
logging.warning("RedisDB.delete " + str(key) + " got exception: " + str(e))
|
logging.warning("RedisDB.delete " + str(key) + " got exception: " + str(e))
|
||||||
self.__open__()
|
self.__open__()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
REDIS_CONN = RedisDB()
|
REDIS_CONN = RedisDB()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -123,7 +123,7 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def put(self, bucket, fnm, binary):
|
def put(self, bucket, fnm, binary, **kwargs):
|
||||||
logging.debug(f"bucket name {bucket}; filename :{fnm}:")
|
logging.debug(f"bucket name {bucket}; filename :{fnm}:")
|
||||||
for _ in range(1):
|
for _ in range(1):
|
||||||
try:
|
try:
|
||||||
@ -140,7 +140,7 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def rm(self, bucket, fnm):
|
def rm(self, bucket, fnm, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.conn.delete_object(Bucket=bucket, Key=fnm)
|
self.conn.delete_object(Bucket=bucket, Key=fnm)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -148,7 +148,7 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def get(self, bucket, fnm):
|
def get(self, bucket, fnm, **kwargs):
|
||||||
for _ in range(1):
|
for _ in range(1):
|
||||||
try:
|
try:
|
||||||
r = self.conn.get_object(Bucket=bucket, Key=fnm)
|
r = self.conn.get_object(Bucket=bucket, Key=fnm)
|
||||||
@ -162,7 +162,7 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def obj_exist(self, bucket, fnm):
|
def obj_exist(self, bucket, fnm, **kwargs):
|
||||||
try:
|
try:
|
||||||
if self.conn.head_object(Bucket=bucket, Key=fnm):
|
if self.conn.head_object(Bucket=bucket, Key=fnm):
|
||||||
return True
|
return True
|
||||||
@ -174,7 +174,7 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def get_presigned_url(self, bucket, fnm, expires):
|
def get_presigned_url(self, bucket, fnm, expires, **kwargs):
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
try:
|
try:
|
||||||
r = self.conn.generate_presigned_url('get_object',
|
r = self.conn.generate_presigned_url('get_object',
|
||||||
|
|||||||
@ -353,7 +353,12 @@ export const useHandleMessageInputChange = () => {
|
|||||||
export const useSelectDerivedMessages = () => {
|
export const useSelectDerivedMessages = () => {
|
||||||
const [derivedMessages, setDerivedMessages] = useState<IMessage[]>([]);
|
const [derivedMessages, setDerivedMessages] = useState<IMessage[]>([]);
|
||||||
|
|
||||||
const ref = useScrollToBottom(derivedMessages);
|
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const { scrollRef, scrollToBottom } = useScrollToBottom(
|
||||||
|
derivedMessages,
|
||||||
|
messageContainerRef,
|
||||||
|
);
|
||||||
|
|
||||||
const addNewestQuestion = useCallback(
|
const addNewestQuestion = useCallback(
|
||||||
(message: Message, answer: string = '') => {
|
(message: Message, answer: string = '') => {
|
||||||
@ -492,7 +497,8 @@ export const useSelectDerivedMessages = () => {
|
|||||||
}, [setDerivedMessages]);
|
}, [setDerivedMessages]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
setDerivedMessages,
|
setDerivedMessages,
|
||||||
addNewestQuestion,
|
addNewestQuestion,
|
||||||
@ -503,6 +509,7 @@ export const useSelectDerivedMessages = () => {
|
|||||||
addNewestOneAnswer,
|
addNewestOneAnswer,
|
||||||
removeMessagesAfterCurrentMessage,
|
removeMessagesAfterCurrentMessage,
|
||||||
removeAllMessages,
|
removeAllMessages,
|
||||||
|
scrollToBottom,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,13 @@ export default {
|
|||||||
nextPage: 'Next',
|
nextPage: 'Next',
|
||||||
add: 'Add',
|
add: 'Add',
|
||||||
promptPlaceholder: `Please input or use / to quickly insert variables.`,
|
promptPlaceholder: `Please input or use / to quickly insert variables.`,
|
||||||
|
mcp: {
|
||||||
|
namePlaceholder: 'My MCP Server',
|
||||||
|
nameRequired:
|
||||||
|
'It must be 1–64 characters long and can only contain letters, numbers, hyphens, and underscores.',
|
||||||
|
urlPlaceholder: 'https://api.example.com/v1/mcp',
|
||||||
|
tokenPlaceholder: 'e.g. eyJhbGciOiJIUzI1Ni...',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
|
|||||||
@ -22,7 +22,8 @@ import { useAwaitCompentData } from '../hooks/use-chat-logic';
|
|||||||
function AgentChatBox() {
|
function AgentChatBox() {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
sendLoading,
|
sendLoading,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
@ -59,7 +60,7 @@ function AgentChatBox() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
||||||
<div className="flex-1 overflow-auto">
|
<div className="flex-1 overflow-auto" ref={messageContainerRef}>
|
||||||
<div>
|
<div>
|
||||||
{/* <Spin spinning={sendLoading}> */}
|
{/* <Spin spinning={sendLoading}> */}
|
||||||
{derivedMessages?.map((message, i) => {
|
{derivedMessages?.map((message, i) => {
|
||||||
@ -106,7 +107,7 @@ function AgentChatBox() {
|
|||||||
})}
|
})}
|
||||||
{/* </Spin> */}
|
{/* </Spin> */}
|
||||||
</div>
|
</div>
|
||||||
<div ref={ref.scrollRef} />
|
<div ref={scrollRef} />
|
||||||
</div>
|
</div>
|
||||||
<NextMessageInput
|
<NextMessageInput
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@ -198,12 +198,14 @@ export const useSendAgentMessage = (
|
|||||||
const prologue = useGetBeginNodePrologue();
|
const prologue = useGetBeginNodePrologue();
|
||||||
const {
|
const {
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
removeLatestMessage,
|
removeLatestMessage,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
addNewestOneQuestion,
|
addNewestOneQuestion,
|
||||||
addNewestOneAnswer,
|
addNewestOneAnswer,
|
||||||
removeAllMessages,
|
removeAllMessages,
|
||||||
|
scrollToBottom,
|
||||||
} = useSelectDerivedMessages();
|
} = useSelectDerivedMessages();
|
||||||
const { addEventList: addEventListFun } = useContext(AgentChatLogContext);
|
const { addEventList: addEventListFun } = useContext(AgentChatLogContext);
|
||||||
const {
|
const {
|
||||||
@ -303,7 +305,18 @@ export const useSendAgentMessage = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
addNewestOneQuestion({ ...msgBody, files: fileList });
|
addNewestOneQuestion({ ...msgBody, files: fileList });
|
||||||
}, [value, done, addNewestOneQuestion, fileList, setValue, sendMessage]);
|
setTimeout(() => {
|
||||||
|
scrollToBottom();
|
||||||
|
}, 100);
|
||||||
|
}, [
|
||||||
|
value,
|
||||||
|
done,
|
||||||
|
addNewestOneQuestion,
|
||||||
|
fileList,
|
||||||
|
setValue,
|
||||||
|
sendMessage,
|
||||||
|
scrollToBottom,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { content, id } = findMessageFromList(answerList);
|
const { content, id } = findMessageFromList(answerList);
|
||||||
@ -337,7 +350,8 @@ export const useSendAgentMessage = (
|
|||||||
value,
|
value,
|
||||||
sendLoading: !done,
|
sendLoading: !done,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
handlePressEnter,
|
handlePressEnter,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import MessageItem from '@/components/message-item';
|
import MessageItem from '@/components/message-item';
|
||||||
import { MessageType } from '@/constants/chat';
|
import { MessageType } from '@/constants/chat';
|
||||||
import { Flex, Spin } from 'antd';
|
import { Flex, Spin } from 'antd';
|
||||||
import { useRef } from 'react';
|
|
||||||
import {
|
import {
|
||||||
useCreateConversationBeforeUploadDocument,
|
useCreateConversationBeforeUploadDocument,
|
||||||
useGetFileIcon,
|
useGetFileIcon,
|
||||||
@ -19,7 +18,6 @@ import {
|
|||||||
useFetchNextDialog,
|
useFetchNextDialog,
|
||||||
useGetChatSearchParams,
|
useGetChatSearchParams,
|
||||||
} from '@/hooks/chat-hooks';
|
} from '@/hooks/chat-hooks';
|
||||||
import { useScrollToBottom } from '@/hooks/logic-hooks';
|
|
||||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -34,10 +32,10 @@ const ChatContainer = ({ controller }: IProps) => {
|
|||||||
const { data: conversation } = useFetchNextConversation();
|
const { data: conversation } = useFetchNextConversation();
|
||||||
const { data: currentDialog } = useFetchNextDialog();
|
const { data: currentDialog } = useFetchNextDialog();
|
||||||
|
|
||||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
loading,
|
loading,
|
||||||
sendLoading,
|
sendLoading,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
@ -47,10 +45,6 @@ const ChatContainer = ({ controller }: IProps) => {
|
|||||||
removeMessageById,
|
removeMessageById,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
} = useSendNextMessage(controller);
|
} = useSendNextMessage(controller);
|
||||||
const { scrollRef, isAtBottom, scrollToBottom } = useScrollToBottom(
|
|
||||||
derivedMessages,
|
|
||||||
messageContainerRef,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
@ -61,11 +55,6 @@ const ChatContainer = ({ controller }: IProps) => {
|
|||||||
const { createConversationBeforeUploadDocument } =
|
const { createConversationBeforeUploadDocument } =
|
||||||
useCreateConversationBeforeUploadDocument();
|
useCreateConversationBeforeUploadDocument();
|
||||||
|
|
||||||
const handleSend = (msg) => {
|
|
||||||
// your send logic
|
|
||||||
setTimeout(scrollToBottom, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||||
|
|||||||
@ -291,7 +291,8 @@ export const useSetConversation = () => {
|
|||||||
|
|
||||||
export const useSelectNextMessages = () => {
|
export const useSelectNextMessages = () => {
|
||||||
const {
|
const {
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
setDerivedMessages,
|
setDerivedMessages,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
addNewestAnswer,
|
addNewestAnswer,
|
||||||
@ -335,7 +336,8 @@ export const useSelectNextMessages = () => {
|
|||||||
}, [conversation.message, conversationId, setDerivedMessages, isNew]);
|
}, [conversation.message, conversationId, setDerivedMessages, isNew]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
loading,
|
loading,
|
||||||
addNewestAnswer,
|
addNewestAnswer,
|
||||||
@ -371,7 +373,8 @@ export const useSendNextMessage = (controller: AbortController) => {
|
|||||||
api.completeConversation,
|
api.completeConversation,
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
loading,
|
loading,
|
||||||
addNewestAnswer,
|
addNewestAnswer,
|
||||||
@ -499,7 +502,8 @@ export const useSendNextMessage = (controller: AbortController) => {
|
|||||||
regenerateMessage,
|
regenerateMessage,
|
||||||
sendLoading: !done,
|
sendLoading: !done,
|
||||||
loading,
|
loading,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
|
|||||||
@ -50,7 +50,8 @@ const ChatContainer = () => {
|
|||||||
handleInputChange,
|
handleInputChange,
|
||||||
value,
|
value,
|
||||||
sendLoading,
|
sendLoading,
|
||||||
ref,
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
hasError,
|
hasError,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
@ -149,6 +150,7 @@ const ChatContainer = () => {
|
|||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6',
|
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6',
|
||||||
)}
|
)}
|
||||||
|
ref={messageContainerRef}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{derivedMessages?.map((message, i) => {
|
{derivedMessages?.map((message, i) => {
|
||||||
@ -203,7 +205,7 @@ const ChatContainer = () => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div ref={ref.scrollRef} />
|
<div ref={scrollRef} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full justify-center mb-8">
|
<div className="flex w-full justify-center mb-8">
|
||||||
<div className="w-5/6">
|
<div className="w-5/6">
|
||||||
|
|||||||
@ -37,20 +37,23 @@ export function useBuildFormSchema() {
|
|||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, {
|
.min(1, {
|
||||||
message: t('common.namePlaceholder'),
|
message: t('common.mcp.namePlaceholder'),
|
||||||
|
})
|
||||||
|
.regex(/^[a-zA-Z0-9_-]{1,64}$/, {
|
||||||
|
message: t('common.mcp.nameRequired'),
|
||||||
})
|
})
|
||||||
.trim(),
|
.trim(),
|
||||||
url: z
|
url: z
|
||||||
.string()
|
.string()
|
||||||
.url()
|
.url()
|
||||||
.min(1, {
|
.min(1, {
|
||||||
message: t('common.namePlaceholder'),
|
message: t('common.mcp.urlPlaceholder'),
|
||||||
})
|
})
|
||||||
.trim(),
|
.trim(),
|
||||||
server_type: z
|
server_type: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, {
|
.min(1, {
|
||||||
message: t('common.namePlaceholder'),
|
message: t('common.pleaseSelect'),
|
||||||
})
|
})
|
||||||
.trim(),
|
.trim(),
|
||||||
authorization_token: z.string().optional(),
|
authorization_token: z.string().optional(),
|
||||||
@ -89,7 +92,7 @@ export function EditMcpForm({
|
|||||||
<FormLabel>{t('common.name')}</FormLabel>
|
<FormLabel>{t('common.name')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.namePlaceholder')}
|
placeholder={t('common.mcp.namePlaceholder')}
|
||||||
{...field}
|
{...field}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
@ -106,7 +109,7 @@ export function EditMcpForm({
|
|||||||
<FormLabel>{t('mcp.url')}</FormLabel>
|
<FormLabel>{t('mcp.url')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.namePlaceholder')}
|
placeholder={t('common.mcp.urlPlaceholder')}
|
||||||
{...field}
|
{...field}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@ -148,7 +151,7 @@ export function EditMcpForm({
|
|||||||
<FormLabel>Authorization Token</FormLabel>
|
<FormLabel>Authorization Token</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.namePlaceholder')}
|
placeholder={t('common.mcp.tokenPlaceholder')}
|
||||||
{...field}
|
{...field}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
type="password"
|
type="password"
|
||||||
|
|||||||
Reference in New Issue
Block a user