mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-04 17:45:07 +08:00
Compare commits
11 Commits
3c224c817b
...
6c2c447a72
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c2c447a72 | |||
| e7022db9a4 | |||
| ca4a0ee1b2 | |||
| 27b0550876 | |||
| 797e03f843 | |||
| b4e06237ef | |||
| 751a13fb64 | |||
| fa7b857aa9 | |||
| 257af75ece | |||
| cbdacf21f6 | |||
| b1f3130519 |
@ -10,11 +10,10 @@ WORKDIR /ragflow
|
|||||||
# Copy models downloaded via download_deps.py
|
# Copy models downloaded via download_deps.py
|
||||||
RUN mkdir -p /ragflow/rag/res/deepdoc /root/.ragflow
|
RUN mkdir -p /ragflow/rag/res/deepdoc /root/.ragflow
|
||||||
RUN --mount=type=bind,from=infiniflow/ragflow_deps:latest,source=/huggingface.co,target=/huggingface.co \
|
RUN --mount=type=bind,from=infiniflow/ragflow_deps:latest,source=/huggingface.co,target=/huggingface.co \
|
||||||
cp /huggingface.co/InfiniFlow/huqie/huqie.txt.trie /ragflow/rag/res/ && \
|
|
||||||
tar --exclude='.*' -cf - \
|
tar --exclude='.*' -cf - \
|
||||||
/huggingface.co/InfiniFlow/text_concat_xgb_v1.0 \
|
/huggingface.co/InfiniFlow/text_concat_xgb_v1.0 \
|
||||||
/huggingface.co/InfiniFlow/deepdoc \
|
/huggingface.co/InfiniFlow/deepdoc \
|
||||||
| tar -xf - --strip-components=3 -C /ragflow/rag/res/deepdoc
|
| tar -xf - --strip-components=3 -C /ragflow/rag/res/deepdoc
|
||||||
|
|
||||||
# https://github.com/chrismattmann/tika-python
|
# https://github.com/chrismattmann/tika-python
|
||||||
# This is the only way to run python-tika without internet access. Without this set, the default is to check the tika version and pull latest every time from Apache.
|
# This is the only way to run python-tika without internet access. Without this set, the default is to check the tika version and pull latest every time from Apache.
|
||||||
|
|||||||
@ -91,9 +91,6 @@ class Graph:
|
|||||||
def load(self):
|
def load(self):
|
||||||
self.components = self.dsl["components"]
|
self.components = self.dsl["components"]
|
||||||
cpn_nms = set([])
|
cpn_nms = set([])
|
||||||
for k, cpn in self.components.items():
|
|
||||||
cpn_nms.add(cpn["obj"]["component_name"])
|
|
||||||
|
|
||||||
for k, cpn in self.components.items():
|
for k, cpn in self.components.items():
|
||||||
cpn_nms.add(cpn["obj"]["component_name"])
|
cpn_nms.add(cpn["obj"]["component_name"])
|
||||||
param = component_class(cpn["obj"]["component_name"] + "Param")()
|
param = component_class(cpn["obj"]["component_name"] + "Param")()
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -30,8 +29,8 @@ from api.db.services.llm_service import LLMBundle
|
|||||||
from api.db.services.tenant_llm_service import TenantLLMService
|
from api.db.services.tenant_llm_service import TenantLLMService
|
||||||
from api.db.services.mcp_server_service import MCPServerService
|
from api.db.services.mcp_server_service import MCPServerService
|
||||||
from common.connection_utils import timeout
|
from common.connection_utils import timeout
|
||||||
from rag.prompts.generator import next_step, COMPLETE_TASK, analyze_task, \
|
from rag.prompts.generator import next_step_async, COMPLETE_TASK, analyze_task_async, \
|
||||||
citation_prompt, reflect, rank_memories, kb_prompt, citation_plus, full_question, message_fit_in, structured_output_prompt
|
citation_prompt, reflect_async, kb_prompt, citation_plus, full_question, message_fit_in, structured_output_prompt
|
||||||
from common.mcp_tool_call_conn import MCPToolCallSession, mcp_tool_metadata_to_openai_tool
|
from common.mcp_tool_call_conn import MCPToolCallSession, mcp_tool_metadata_to_openai_tool
|
||||||
from agent.component.llm import LLMParam, LLM
|
from agent.component.llm import LLMParam, LLM
|
||||||
|
|
||||||
@ -154,96 +153,19 @@ class Agent(LLM, ToolBase):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _force_format_to_schema(self, text: str, schema_prompt: str) -> str:
|
async def _force_format_to_schema_async(self, text: str, schema_prompt: str) -> str:
|
||||||
fmt_msgs = [
|
fmt_msgs = [
|
||||||
{"role": "system", "content": schema_prompt + "\nIMPORTANT: Output ONLY valid JSON. No markdown, no extra text."},
|
{"role": "system", "content": schema_prompt + "\nIMPORTANT: Output ONLY valid JSON. No markdown, no extra text."},
|
||||||
{"role": "user", "content": text},
|
{"role": "user", "content": text},
|
||||||
]
|
]
|
||||||
_, fmt_msgs = message_fit_in(fmt_msgs, int(self.chat_mdl.max_length * 0.97))
|
_, fmt_msgs = message_fit_in(fmt_msgs, int(self.chat_mdl.max_length * 0.97))
|
||||||
return self._generate(fmt_msgs)
|
return await self._generate_async(fmt_msgs)
|
||||||
|
|
||||||
|
def _invoke(self, **kwargs):
|
||||||
|
return asyncio.run(self._invoke_async(**kwargs))
|
||||||
|
|
||||||
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 20*60)))
|
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 20*60)))
|
||||||
def _invoke(self, **kwargs):
|
|
||||||
if self.check_if_canceled("Agent processing"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if kwargs.get("user_prompt"):
|
|
||||||
usr_pmt = ""
|
|
||||||
if kwargs.get("reasoning"):
|
|
||||||
usr_pmt += "\nREASONING:\n{}\n".format(kwargs["reasoning"])
|
|
||||||
if kwargs.get("context"):
|
|
||||||
usr_pmt += "\nCONTEXT:\n{}\n".format(kwargs["context"])
|
|
||||||
if usr_pmt:
|
|
||||||
usr_pmt += "\nQUERY:\n{}\n".format(str(kwargs["user_prompt"]))
|
|
||||||
else:
|
|
||||||
usr_pmt = str(kwargs["user_prompt"])
|
|
||||||
self._param.prompts = [{"role": "user", "content": usr_pmt}]
|
|
||||||
|
|
||||||
if not self.tools:
|
|
||||||
if self.check_if_canceled("Agent processing"):
|
|
||||||
return
|
|
||||||
return LLM._invoke(self, **kwargs)
|
|
||||||
|
|
||||||
prompt, msg, user_defined_prompt = self._prepare_prompt_variables()
|
|
||||||
output_schema = self._get_output_schema()
|
|
||||||
schema_prompt = ""
|
|
||||||
if output_schema:
|
|
||||||
schema = json.dumps(output_schema, ensure_ascii=False, indent=2)
|
|
||||||
schema_prompt = structured_output_prompt(schema)
|
|
||||||
|
|
||||||
downstreams = self._canvas.get_component(self._id)["downstream"] if self._canvas.get_component(self._id) else []
|
|
||||||
ex = self.exception_handler()
|
|
||||||
if any([self._canvas.get_component_obj(cid).component_name.lower()=="message" for cid in downstreams]) and not (ex and ex["goto"]) and not output_schema:
|
|
||||||
self.set_output("content", partial(self.stream_output_with_tools, prompt, msg, user_defined_prompt))
|
|
||||||
return
|
|
||||||
|
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
|
||||||
use_tools = []
|
|
||||||
ans = ""
|
|
||||||
for delta_ans, tk in self._react_with_tools_streamly(prompt, msg, use_tools, user_defined_prompt,schema_prompt=schema_prompt):
|
|
||||||
if self.check_if_canceled("Agent processing"):
|
|
||||||
return
|
|
||||||
ans += delta_ans
|
|
||||||
|
|
||||||
if ans.find("**ERROR**") >= 0:
|
|
||||||
logging.error(f"Agent._chat got error. response: {ans}")
|
|
||||||
if self.get_exception_default_value():
|
|
||||||
self.set_output("content", self.get_exception_default_value())
|
|
||||||
else:
|
|
||||||
self.set_output("_ERROR", ans)
|
|
||||||
return
|
|
||||||
|
|
||||||
if output_schema:
|
|
||||||
error = ""
|
|
||||||
for _ in range(self._param.max_retries + 1):
|
|
||||||
try:
|
|
||||||
def clean_formated_answer(ans: str) -> str:
|
|
||||||
ans = re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
|
||||||
ans = re.sub(r"^.*```json", "", ans, flags=re.DOTALL)
|
|
||||||
return re.sub(r"```\n*$", "", ans, flags=re.DOTALL)
|
|
||||||
obj = json_repair.loads(clean_formated_answer(ans))
|
|
||||||
self.set_output("structured", obj)
|
|
||||||
if use_tools:
|
|
||||||
self.set_output("use_tools", use_tools)
|
|
||||||
return obj
|
|
||||||
except Exception:
|
|
||||||
error = "The answer cannot be parsed as JSON"
|
|
||||||
ans = self._force_format_to_schema(ans, schema_prompt)
|
|
||||||
if ans.find("**ERROR**") >= 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.set_output("_ERROR", error)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.set_output("content", ans)
|
|
||||||
if use_tools:
|
|
||||||
self.set_output("use_tools", use_tools)
|
|
||||||
return ans
|
|
||||||
|
|
||||||
async def _invoke_async(self, **kwargs):
|
async def _invoke_async(self, **kwargs):
|
||||||
"""
|
|
||||||
Async entry: reuse existing logic but offload heavy sync parts via async wrappers to reduce blocking.
|
|
||||||
"""
|
|
||||||
if self.check_if_canceled("Agent processing"):
|
if self.check_if_canceled("Agent processing"):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -262,7 +184,7 @@ class Agent(LLM, ToolBase):
|
|||||||
if not self.tools:
|
if not self.tools:
|
||||||
if self.check_if_canceled("Agent processing"):
|
if self.check_if_canceled("Agent processing"):
|
||||||
return
|
return
|
||||||
return await asyncio.to_thread(LLM._invoke, self, **kwargs)
|
return await LLM._invoke_async(self, **kwargs)
|
||||||
|
|
||||||
prompt, msg, user_defined_prompt = self._prepare_prompt_variables()
|
prompt, msg, user_defined_prompt = self._prepare_prompt_variables()
|
||||||
output_schema = self._get_output_schema()
|
output_schema = self._get_output_schema()
|
||||||
@ -274,13 +196,13 @@ class Agent(LLM, ToolBase):
|
|||||||
downstreams = self._canvas.get_component(self._id)["downstream"] if self._canvas.get_component(self._id) else []
|
downstreams = self._canvas.get_component(self._id)["downstream"] if self._canvas.get_component(self._id) else []
|
||||||
ex = self.exception_handler()
|
ex = self.exception_handler()
|
||||||
if any([self._canvas.get_component_obj(cid).component_name.lower()=="message" for cid in downstreams]) and not (ex and ex["goto"]) and not output_schema:
|
if any([self._canvas.get_component_obj(cid).component_name.lower()=="message" for cid in downstreams]) and not (ex and ex["goto"]) and not output_schema:
|
||||||
self.set_output("content", partial(self.stream_output_with_tools_async, prompt, msg, user_defined_prompt))
|
self.set_output("content", partial(self.stream_output_with_tools_async, prompt, deepcopy(msg), user_defined_prompt))
|
||||||
return
|
return
|
||||||
|
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
||||||
use_tools = []
|
use_tools = []
|
||||||
ans = ""
|
ans = ""
|
||||||
async for delta_ans, tk in self._react_with_tools_streamly_async(prompt, msg, use_tools, user_defined_prompt, schema_prompt=schema_prompt):
|
async for delta_ans, _tk in self._react_with_tools_streamly_async(prompt, msg, use_tools, user_defined_prompt,schema_prompt=schema_prompt):
|
||||||
if self.check_if_canceled("Agent processing"):
|
if self.check_if_canceled("Agent processing"):
|
||||||
return
|
return
|
||||||
ans += delta_ans
|
ans += delta_ans
|
||||||
@ -308,7 +230,7 @@ class Agent(LLM, ToolBase):
|
|||||||
return obj
|
return obj
|
||||||
except Exception:
|
except Exception:
|
||||||
error = "The answer cannot be parsed as JSON"
|
error = "The answer cannot be parsed as JSON"
|
||||||
ans = self._force_format_to_schema(ans, schema_prompt)
|
ans = await self._force_format_to_schema_async(ans, schema_prompt)
|
||||||
if ans.find("**ERROR**") >= 0:
|
if ans.find("**ERROR**") >= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -320,28 +242,6 @@ class Agent(LLM, ToolBase):
|
|||||||
self.set_output("use_tools", use_tools)
|
self.set_output("use_tools", use_tools)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def stream_output_with_tools(self, prompt, msg, user_defined_prompt={}):
|
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
|
||||||
answer_without_toolcall = ""
|
|
||||||
use_tools = []
|
|
||||||
for delta_ans,_ in self._react_with_tools_streamly(prompt, msg, use_tools, user_defined_prompt):
|
|
||||||
if self.check_if_canceled("Agent streaming"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if delta_ans.find("**ERROR**") >= 0:
|
|
||||||
if self.get_exception_default_value():
|
|
||||||
self.set_output("content", self.get_exception_default_value())
|
|
||||||
yield self.get_exception_default_value()
|
|
||||||
else:
|
|
||||||
self.set_output("_ERROR", delta_ans)
|
|
||||||
return
|
|
||||||
answer_without_toolcall += delta_ans
|
|
||||||
yield delta_ans
|
|
||||||
|
|
||||||
self.set_output("content", answer_without_toolcall)
|
|
||||||
if use_tools:
|
|
||||||
self.set_output("use_tools", use_tools)
|
|
||||||
|
|
||||||
async def stream_output_with_tools_async(self, prompt, msg, user_defined_prompt={}):
|
async def stream_output_with_tools_async(self, prompt, msg, user_defined_prompt={}):
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
||||||
answer_without_toolcall = ""
|
answer_without_toolcall = ""
|
||||||
@ -365,64 +265,22 @@ class Agent(LLM, ToolBase):
|
|||||||
self.set_output("use_tools", use_tools)
|
self.set_output("use_tools", use_tools)
|
||||||
|
|
||||||
async def _react_with_tools_streamly_async(self, prompt, history: list[dict], use_tools, user_defined_prompt={}, schema_prompt: str = ""):
|
async def _react_with_tools_streamly_async(self, prompt, history: list[dict], use_tools, user_defined_prompt={}, schema_prompt: str = ""):
|
||||||
"""
|
|
||||||
Async wrapper that offloads synchronous flow to a thread, yielding results without blocking the event loop.
|
|
||||||
"""
|
|
||||||
loop = asyncio.get_running_loop()
|
|
||||||
queue: asyncio.Queue = asyncio.Queue()
|
|
||||||
|
|
||||||
def worker():
|
|
||||||
try:
|
|
||||||
for delta_ans, tk in self._react_with_tools_streamly(prompt, history, use_tools, user_defined_prompt, schema_prompt=schema_prompt):
|
|
||||||
asyncio.run_coroutine_threadsafe(queue.put((delta_ans, tk)), loop)
|
|
||||||
except Exception as e:
|
|
||||||
asyncio.run_coroutine_threadsafe(queue.put(e), loop)
|
|
||||||
finally:
|
|
||||||
asyncio.run_coroutine_threadsafe(queue.put(StopAsyncIteration), loop)
|
|
||||||
|
|
||||||
await asyncio.to_thread(worker)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
item = await queue.get()
|
|
||||||
if item is StopAsyncIteration:
|
|
||||||
break
|
|
||||||
if isinstance(item, Exception):
|
|
||||||
raise item
|
|
||||||
yield item
|
|
||||||
|
|
||||||
def _gen_citations(self, text):
|
|
||||||
retrievals = self._canvas.get_reference()
|
|
||||||
retrievals = {"chunks": list(retrievals["chunks"].values()), "doc_aggs": list(retrievals["doc_aggs"].values())}
|
|
||||||
formated_refer = kb_prompt(retrievals, self.chat_mdl.max_length, True)
|
|
||||||
for delta_ans in self._generate_streamly([{"role": "system", "content": citation_plus("\n\n".join(formated_refer))},
|
|
||||||
{"role": "user", "content": text}
|
|
||||||
]):
|
|
||||||
yield delta_ans
|
|
||||||
|
|
||||||
def _react_with_tools_streamly(self, prompt, history: list[dict], use_tools, user_defined_prompt={}, schema_prompt: str = ""):
|
|
||||||
token_count = 0
|
token_count = 0
|
||||||
tool_metas = self.tool_meta
|
tool_metas = self.tool_meta
|
||||||
hist = deepcopy(history)
|
hist = deepcopy(history)
|
||||||
last_calling = ""
|
last_calling = ""
|
||||||
if len(hist) > 3:
|
if len(hist) > 3:
|
||||||
st = timer()
|
st = timer()
|
||||||
user_request = full_question(messages=history, chat_mdl=self.chat_mdl)
|
user_request = await asyncio.to_thread(full_question, messages=history, chat_mdl=self.chat_mdl)
|
||||||
self.callback("Multi-turn conversation optimization", {}, user_request, elapsed_time=timer()-st)
|
self.callback("Multi-turn conversation optimization", {}, user_request, elapsed_time=timer()-st)
|
||||||
else:
|
else:
|
||||||
user_request = history[-1]["content"]
|
user_request = history[-1]["content"]
|
||||||
|
|
||||||
def use_tool(name, args):
|
async def use_tool_async(name, args):
|
||||||
nonlocal hist, use_tools, token_count,last_calling,user_request
|
nonlocal hist, use_tools, last_calling
|
||||||
logging.info(f"{last_calling=} == {name=}")
|
logging.info(f"{last_calling=} == {name=}")
|
||||||
# Summarize of function calling
|
|
||||||
#if all([
|
|
||||||
# isinstance(self.toolcall_session.get_tool_obj(name), Agent),
|
|
||||||
# last_calling,
|
|
||||||
# last_calling != name
|
|
||||||
#]):
|
|
||||||
# self.toolcall_session.get_tool_obj(name).add2system_prompt(f"The chat history with other agents are as following: \n" + self.get_useful_memory(user_request, str(args["user_prompt"]),user_defined_prompt))
|
|
||||||
last_calling = name
|
last_calling = name
|
||||||
tool_response = self.toolcall_session.tool_call(name, args)
|
tool_response = await self.toolcall_session.tool_call_async(name, args)
|
||||||
use_tools.append({
|
use_tools.append({
|
||||||
"name": name,
|
"name": name,
|
||||||
"arguments": args,
|
"arguments": args,
|
||||||
@ -433,7 +291,7 @@ class Agent(LLM, ToolBase):
|
|||||||
|
|
||||||
return name, tool_response
|
return name, tool_response
|
||||||
|
|
||||||
def complete():
|
async def complete():
|
||||||
nonlocal hist
|
nonlocal hist
|
||||||
need2cite = self._param.cite and self._canvas.get_reference()["chunks"] and self._id.find("-->") < 0
|
need2cite = self._param.cite and self._canvas.get_reference()["chunks"] and self._id.find("-->") < 0
|
||||||
if schema_prompt:
|
if schema_prompt:
|
||||||
@ -451,7 +309,7 @@ class Agent(LLM, ToolBase):
|
|||||||
if len(hist) > 12:
|
if len(hist) > 12:
|
||||||
_hist = [hist[0], hist[1], *hist[-10:]]
|
_hist = [hist[0], hist[1], *hist[-10:]]
|
||||||
entire_txt = ""
|
entire_txt = ""
|
||||||
for delta_ans in self._generate_streamly(_hist):
|
async for delta_ans in self._generate_streamly_async(_hist):
|
||||||
if not need2cite or cited:
|
if not need2cite or cited:
|
||||||
yield delta_ans, 0
|
yield delta_ans, 0
|
||||||
entire_txt += delta_ans
|
entire_txt += delta_ans
|
||||||
@ -460,7 +318,7 @@ class Agent(LLM, ToolBase):
|
|||||||
|
|
||||||
st = timer()
|
st = timer()
|
||||||
txt = ""
|
txt = ""
|
||||||
for delta_ans in self._gen_citations(entire_txt):
|
async for delta_ans in self._gen_citations_async(entire_txt):
|
||||||
if self.check_if_canceled("Agent streaming"):
|
if self.check_if_canceled("Agent streaming"):
|
||||||
return
|
return
|
||||||
yield delta_ans, 0
|
yield delta_ans, 0
|
||||||
@ -475,14 +333,14 @@ class Agent(LLM, ToolBase):
|
|||||||
hist.append({"role": "user", "content": content})
|
hist.append({"role": "user", "content": content})
|
||||||
|
|
||||||
st = timer()
|
st = timer()
|
||||||
task_desc = analyze_task(self.chat_mdl, prompt, user_request, tool_metas, user_defined_prompt)
|
task_desc = await analyze_task_async(self.chat_mdl, prompt, user_request, tool_metas, user_defined_prompt)
|
||||||
self.callback("analyze_task", {}, task_desc, elapsed_time=timer()-st)
|
self.callback("analyze_task", {}, task_desc, elapsed_time=timer()-st)
|
||||||
for _ in range(self._param.max_rounds + 1):
|
for _ in range(self._param.max_rounds + 1):
|
||||||
if self.check_if_canceled("Agent streaming"):
|
if self.check_if_canceled("Agent streaming"):
|
||||||
return
|
return
|
||||||
response, tk = next_step(self.chat_mdl, hist, tool_metas, task_desc, user_defined_prompt)
|
response, tk = await next_step_async(self.chat_mdl, hist, tool_metas, task_desc, user_defined_prompt)
|
||||||
# self.callback("next_step", {}, str(response)[:256]+"...")
|
# self.callback("next_step", {}, str(response)[:256]+"...")
|
||||||
token_count += tk
|
token_count += tk or 0
|
||||||
hist.append({"role": "assistant", "content": response})
|
hist.append({"role": "assistant", "content": response})
|
||||||
try:
|
try:
|
||||||
functions = json_repair.loads(re.sub(r"```.*", "", response))
|
functions = json_repair.loads(re.sub(r"```.*", "", response))
|
||||||
@ -491,23 +349,24 @@ class Agent(LLM, ToolBase):
|
|||||||
for f in functions:
|
for f in functions:
|
||||||
if not isinstance(f, dict):
|
if not isinstance(f, dict):
|
||||||
raise TypeError(f"An object type should be returned, but `{f}`")
|
raise TypeError(f"An object type should be returned, but `{f}`")
|
||||||
with ThreadPoolExecutor(max_workers=5) as executor:
|
|
||||||
thr = []
|
|
||||||
for func in functions:
|
|
||||||
name = func["name"]
|
|
||||||
args = func["arguments"]
|
|
||||||
if name == COMPLETE_TASK:
|
|
||||||
append_user_content(hist, f"Respond with a formal answer. FORGET(DO NOT mention) about `{COMPLETE_TASK}`. The language for the response MUST be as the same as the first user request.\n")
|
|
||||||
for txt, tkcnt in complete():
|
|
||||||
yield txt, tkcnt
|
|
||||||
return
|
|
||||||
|
|
||||||
thr.append(executor.submit(use_tool, name, args))
|
tool_tasks = []
|
||||||
|
for func in functions:
|
||||||
|
name = func["name"]
|
||||||
|
args = func["arguments"]
|
||||||
|
if name == COMPLETE_TASK:
|
||||||
|
append_user_content(hist, f"Respond with a formal answer. FORGET(DO NOT mention) about `{COMPLETE_TASK}`. The language for the response MUST be as the same as the first user request.\n")
|
||||||
|
async for txt, tkcnt in complete():
|
||||||
|
yield txt, tkcnt
|
||||||
|
return
|
||||||
|
|
||||||
st = timer()
|
tool_tasks.append(asyncio.create_task(use_tool_async(name, args)))
|
||||||
reflection = reflect(self.chat_mdl, hist, [th.result() for th in thr], user_defined_prompt)
|
|
||||||
append_user_content(hist, reflection)
|
results = await asyncio.gather(*tool_tasks) if tool_tasks else []
|
||||||
self.callback("reflection", {}, str(reflection), elapsed_time=timer()-st)
|
st = timer()
|
||||||
|
reflection = await reflect_async(self.chat_mdl, hist, results, user_defined_prompt)
|
||||||
|
append_user_content(hist, reflection)
|
||||||
|
self.callback("reflection", {}, str(reflection), elapsed_time=timer()-st)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(msg=f"Wrong JSON argument format in LLM ReAct response: {e}")
|
logging.exception(msg=f"Wrong JSON argument format in LLM ReAct response: {e}")
|
||||||
@ -531,21 +390,17 @@ Respond immediately with your final comprehensive answer.
|
|||||||
return
|
return
|
||||||
append_user_content(hist, final_instruction)
|
append_user_content(hist, final_instruction)
|
||||||
|
|
||||||
for txt, tkcnt in complete():
|
async for txt, tkcnt in complete():
|
||||||
yield txt, tkcnt
|
yield txt, tkcnt
|
||||||
|
|
||||||
def get_useful_memory(self, goal: str, sub_goal:str, topn=3, user_defined_prompt:dict={}) -> str:
|
async def _gen_citations_async(self, text):
|
||||||
# self.callback("get_useful_memory", {"topn": 3}, "...")
|
retrievals = self._canvas.get_reference()
|
||||||
mems = self._canvas.get_memory()
|
retrievals = {"chunks": list(retrievals["chunks"].values()), "doc_aggs": list(retrievals["doc_aggs"].values())}
|
||||||
rank = rank_memories(self.chat_mdl, goal, sub_goal, [summ for (user, assist, summ) in mems], user_defined_prompt)
|
formated_refer = kb_prompt(retrievals, self.chat_mdl.max_length, True)
|
||||||
try:
|
async for delta_ans in self._generate_streamly_async([{"role": "system", "content": citation_plus("\n\n".join(formated_refer))},
|
||||||
rank = json_repair.loads(re.sub(r"```.*", "", rank))[:topn]
|
{"role": "user", "content": text}
|
||||||
mems = [mems[r] for r in rank]
|
]):
|
||||||
return "\n\n".join([f"User: {u}\nAgent: {a}" for u, a,_ in mems])
|
yield delta_ans
|
||||||
except Exception as e:
|
|
||||||
logging.exception(e)
|
|
||||||
|
|
||||||
return "Error occurred."
|
|
||||||
|
|
||||||
def reset(self, only_output=False):
|
def reset(self, only_output=False):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -327,7 +327,7 @@ class LLM(ComponentBase):
|
|||||||
self.set_output("content", answer)
|
self.set_output("content", answer)
|
||||||
|
|
||||||
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)))
|
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)))
|
||||||
def _invoke(self, **kwargs):
|
async def _invoke_async(self, **kwargs):
|
||||||
if self.check_if_canceled("LLM processing"):
|
if self.check_if_canceled("LLM processing"):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -338,22 +338,25 @@ class LLM(ComponentBase):
|
|||||||
|
|
||||||
prompt, msg, _ = self._prepare_prompt_variables()
|
prompt, msg, _ = self._prepare_prompt_variables()
|
||||||
error: str = ""
|
error: str = ""
|
||||||
output_structure=None
|
output_structure = None
|
||||||
try:
|
try:
|
||||||
output_structure = self._param.outputs['structured']
|
output_structure = self._param.outputs["structured"]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if output_structure and isinstance(output_structure, dict) and output_structure.get("properties") and len(output_structure["properties"]) > 0:
|
if output_structure and isinstance(output_structure, dict) and output_structure.get("properties") and len(output_structure["properties"]) > 0:
|
||||||
schema=json.dumps(output_structure, ensure_ascii=False, indent=2)
|
schema = json.dumps(output_structure, ensure_ascii=False, indent=2)
|
||||||
prompt += structured_output_prompt(schema)
|
prompt_with_schema = prompt + structured_output_prompt(schema)
|
||||||
for _ in range(self._param.max_retries+1):
|
for _ in range(self._param.max_retries + 1):
|
||||||
if self.check_if_canceled("LLM processing"):
|
if self.check_if_canceled("LLM processing"):
|
||||||
return
|
return
|
||||||
|
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
_, msg_fit = message_fit_in(
|
||||||
|
[{"role": "system", "content": prompt_with_schema}, *deepcopy(msg)],
|
||||||
|
int(self.chat_mdl.max_length * 0.97),
|
||||||
|
)
|
||||||
error = ""
|
error = ""
|
||||||
ans = self._generate(msg)
|
ans = await self._generate_async(msg_fit)
|
||||||
msg.pop(0)
|
msg_fit.pop(0)
|
||||||
if ans.find("**ERROR**") >= 0:
|
if ans.find("**ERROR**") >= 0:
|
||||||
logging.error(f"LLM response error: {ans}")
|
logging.error(f"LLM response error: {ans}")
|
||||||
error = ans
|
error = ans
|
||||||
@ -362,7 +365,7 @@ class LLM(ComponentBase):
|
|||||||
self.set_output("structured", json_repair.loads(clean_formated_answer(ans)))
|
self.set_output("structured", json_repair.loads(clean_formated_answer(ans)))
|
||||||
return
|
return
|
||||||
except Exception:
|
except Exception:
|
||||||
msg.append({"role": "user", "content": "The answer can't not be parsed as JSON"})
|
msg_fit.append({"role": "user", "content": "The answer can't not be parsed as JSON"})
|
||||||
error = "The answer can't not be parsed as JSON"
|
error = "The answer can't not be parsed as JSON"
|
||||||
if error:
|
if error:
|
||||||
self.set_output("_ERROR", error)
|
self.set_output("_ERROR", error)
|
||||||
@ -370,18 +373,23 @@ class LLM(ComponentBase):
|
|||||||
|
|
||||||
downstreams = self._canvas.get_component(self._id)["downstream"] if self._canvas.get_component(self._id) else []
|
downstreams = self._canvas.get_component(self._id)["downstream"] if self._canvas.get_component(self._id) else []
|
||||||
ex = self.exception_handler()
|
ex = self.exception_handler()
|
||||||
if any([self._canvas.get_component_obj(cid).component_name.lower()=="message" for cid in downstreams]) and not (ex and ex["goto"]):
|
if any([self._canvas.get_component_obj(cid).component_name.lower() == "message" for cid in downstreams]) and not (
|
||||||
self.set_output("content", partial(self._stream_output_async, prompt, msg))
|
ex and ex["goto"]
|
||||||
|
):
|
||||||
|
self.set_output("content", partial(self._stream_output_async, prompt, deepcopy(msg)))
|
||||||
return
|
return
|
||||||
|
|
||||||
for _ in range(self._param.max_retries+1):
|
error = ""
|
||||||
|
for _ in range(self._param.max_retries + 1):
|
||||||
if self.check_if_canceled("LLM processing"):
|
if self.check_if_canceled("LLM processing"):
|
||||||
return
|
return
|
||||||
|
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
_, msg_fit = message_fit_in(
|
||||||
|
[{"role": "system", "content": prompt}, *deepcopy(msg)], int(self.chat_mdl.max_length * 0.97)
|
||||||
|
)
|
||||||
error = ""
|
error = ""
|
||||||
ans = self._generate(msg)
|
ans = await self._generate_async(msg_fit)
|
||||||
msg.pop(0)
|
msg_fit.pop(0)
|
||||||
if ans.find("**ERROR**") >= 0:
|
if ans.find("**ERROR**") >= 0:
|
||||||
logging.error(f"LLM response error: {ans}")
|
logging.error(f"LLM response error: {ans}")
|
||||||
error = ans
|
error = ans
|
||||||
@ -395,23 +403,9 @@ class LLM(ComponentBase):
|
|||||||
else:
|
else:
|
||||||
self.set_output("_ERROR", error)
|
self.set_output("_ERROR", error)
|
||||||
|
|
||||||
def _stream_output(self, prompt, msg):
|
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)))
|
||||||
_, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(self.chat_mdl.max_length * 0.97))
|
def _invoke(self, **kwargs):
|
||||||
answer = ""
|
return asyncio.run(self._invoke_async(**kwargs))
|
||||||
for ans in self._generate_streamly(msg):
|
|
||||||
if self.check_if_canceled("LLM streaming"):
|
|
||||||
return
|
|
||||||
|
|
||||||
if ans.find("**ERROR**") >= 0:
|
|
||||||
if self.get_exception_default_value():
|
|
||||||
self.set_output("content", self.get_exception_default_value())
|
|
||||||
yield self.get_exception_default_value()
|
|
||||||
else:
|
|
||||||
self.set_output("_ERROR", ans)
|
|
||||||
return
|
|
||||||
yield ans
|
|
||||||
answer += ans
|
|
||||||
self.set_output("content", answer)
|
|
||||||
|
|
||||||
def add_memory(self, user:str, assist:str, func_name: str, params: dict, results: str, user_defined_prompt:dict={}):
|
def add_memory(self, user:str, assist:str, func_name: str, params: dict, results: str, user_defined_prompt:dict={}):
|
||||||
summ = tool_call_summary(self.chat_mdl, func_name, params, results, user_defined_prompt)
|
summ = tool_call_summary(self.chat_mdl, func_name, params, results, user_defined_prompt)
|
||||||
|
|||||||
@ -49,16 +49,19 @@ class LLMToolPluginCallSession(ToolCallSession):
|
|||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
def tool_call(self, name: str, arguments: dict[str, Any]) -> Any:
|
def tool_call(self, name: str, arguments: dict[str, Any]) -> Any:
|
||||||
|
return asyncio.run(self.tool_call_async(name, arguments))
|
||||||
|
|
||||||
|
async def tool_call_async(self, name: str, arguments: dict[str, Any]) -> Any:
|
||||||
assert name in self.tools_map, f"LLM tool {name} does not exist"
|
assert name in self.tools_map, f"LLM tool {name} does not exist"
|
||||||
st = timer()
|
st = timer()
|
||||||
tool_obj = self.tools_map[name]
|
tool_obj = self.tools_map[name]
|
||||||
if isinstance(tool_obj, MCPToolCallSession):
|
if isinstance(tool_obj, MCPToolCallSession):
|
||||||
resp = tool_obj.tool_call(name, arguments, 60)
|
resp = await asyncio.to_thread(tool_obj.tool_call, name, arguments, 60)
|
||||||
else:
|
else:
|
||||||
if hasattr(tool_obj, "invoke_async") and asyncio.iscoroutinefunction(tool_obj.invoke_async):
|
if hasattr(tool_obj, "invoke_async") and asyncio.iscoroutinefunction(tool_obj.invoke_async):
|
||||||
resp = asyncio.run(tool_obj.invoke_async(**arguments))
|
resp = await tool_obj.invoke_async(**arguments)
|
||||||
else:
|
else:
|
||||||
resp = asyncio.run(asyncio.to_thread(tool_obj.invoke, **arguments))
|
resp = await asyncio.to_thread(tool_obj.invoke, **arguments)
|
||||||
|
|
||||||
self.callback(name, arguments, resp, elapsed_time=timer()-st)
|
self.callback(name, arguments, resp, elapsed_time=timer()-st)
|
||||||
return resp
|
return resp
|
||||||
|
|||||||
@ -321,9 +321,7 @@ async def update_doc(tenant_id, dataset_id, document_id):
|
|||||||
try:
|
try:
|
||||||
if not DocumentService.update_by_id(doc.id, {"status": str(status)}):
|
if not DocumentService.update_by_id(doc.id, {"status": str(status)}):
|
||||||
return get_error_data_result(message="Database error (Document update)!")
|
return get_error_data_result(message="Database error (Document update)!")
|
||||||
|
|
||||||
settings.docStoreConn.update({"doc_id": doc.id}, {"available_int": status}, search.index_name(kb.tenant_id), doc.kb_id)
|
settings.docStoreConn.update({"doc_id": doc.id}, {"available_int": status}, search.index_name(kb.tenant_id), doc.kb_id)
|
||||||
return get_result(data=True)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return server_error_response(e)
|
return server_error_response(e)
|
||||||
|
|
||||||
@ -350,12 +348,10 @@ async def update_doc(tenant_id, dataset_id, document_id):
|
|||||||
}
|
}
|
||||||
renamed_doc = {}
|
renamed_doc = {}
|
||||||
for key, value in doc.to_dict().items():
|
for key, value in doc.to_dict().items():
|
||||||
if key == "run":
|
|
||||||
renamed_doc["run"] = run_mapping.get(str(value))
|
|
||||||
new_key = key_mapping.get(key, key)
|
new_key = key_mapping.get(key, key)
|
||||||
renamed_doc[new_key] = value
|
renamed_doc[new_key] = value
|
||||||
if key == "run":
|
if key == "run":
|
||||||
renamed_doc["run"] = run_mapping.get(value)
|
renamed_doc["run"] = run_mapping.get(str(value))
|
||||||
|
|
||||||
return get_result(data=renamed_doc)
|
return get_result(data=renamed_doc)
|
||||||
|
|
||||||
|
|||||||
@ -148,6 +148,7 @@ class Storage(Enum):
|
|||||||
AWS_S3 = 4
|
AWS_S3 = 4
|
||||||
OSS = 5
|
OSS = 5
|
||||||
OPENDAL = 6
|
OPENDAL = 6
|
||||||
|
GCS = 7
|
||||||
|
|
||||||
# environment
|
# environment
|
||||||
# ENV_STRONG_TEST_COUNT = "STRONG_TEST_COUNT"
|
# ENV_STRONG_TEST_COUNT = "STRONG_TEST_COUNT"
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import rag.utils.ob_conn
|
|||||||
import rag.utils.opensearch_conn
|
import rag.utils.opensearch_conn
|
||||||
from rag.utils.azure_sas_conn import RAGFlowAzureSasBlob
|
from rag.utils.azure_sas_conn import RAGFlowAzureSasBlob
|
||||||
from rag.utils.azure_spn_conn import RAGFlowAzureSpnBlob
|
from rag.utils.azure_spn_conn import RAGFlowAzureSpnBlob
|
||||||
|
from rag.utils.gcs_conn import RAGFlowGCS
|
||||||
from rag.utils.minio_conn import RAGFlowMinio
|
from rag.utils.minio_conn import RAGFlowMinio
|
||||||
from rag.utils.opendal_conn import OpenDALStorage
|
from rag.utils.opendal_conn import OpenDALStorage
|
||||||
from rag.utils.s3_conn import RAGFlowS3
|
from rag.utils.s3_conn import RAGFlowS3
|
||||||
@ -109,6 +110,7 @@ MINIO = {}
|
|||||||
OB = {}
|
OB = {}
|
||||||
OSS = {}
|
OSS = {}
|
||||||
OS = {}
|
OS = {}
|
||||||
|
GCS = {}
|
||||||
|
|
||||||
DOC_MAXIMUM_SIZE: int = 128 * 1024 * 1024
|
DOC_MAXIMUM_SIZE: int = 128 * 1024 * 1024
|
||||||
DOC_BULK_SIZE: int = 4
|
DOC_BULK_SIZE: int = 4
|
||||||
@ -151,7 +153,8 @@ class StorageFactory:
|
|||||||
Storage.AZURE_SAS: RAGFlowAzureSasBlob,
|
Storage.AZURE_SAS: RAGFlowAzureSasBlob,
|
||||||
Storage.AWS_S3: RAGFlowS3,
|
Storage.AWS_S3: RAGFlowS3,
|
||||||
Storage.OSS: RAGFlowOSS,
|
Storage.OSS: RAGFlowOSS,
|
||||||
Storage.OPENDAL: OpenDALStorage
|
Storage.OPENDAL: OpenDALStorage,
|
||||||
|
Storage.GCS: RAGFlowGCS,
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -250,7 +253,7 @@ def init_settings():
|
|||||||
else:
|
else:
|
||||||
raise Exception(f"Not supported doc engine: {DOC_ENGINE}")
|
raise Exception(f"Not supported doc engine: {DOC_ENGINE}")
|
||||||
|
|
||||||
global AZURE, S3, MINIO, OSS
|
global AZURE, S3, MINIO, OSS, GCS
|
||||||
if STORAGE_IMPL_TYPE in ['AZURE_SPN', 'AZURE_SAS']:
|
if STORAGE_IMPL_TYPE in ['AZURE_SPN', 'AZURE_SAS']:
|
||||||
AZURE = get_base_config("azure", {})
|
AZURE = get_base_config("azure", {})
|
||||||
elif STORAGE_IMPL_TYPE == 'AWS_S3':
|
elif STORAGE_IMPL_TYPE == 'AWS_S3':
|
||||||
@ -259,6 +262,8 @@ def init_settings():
|
|||||||
MINIO = decrypt_database_config(name="minio")
|
MINIO = decrypt_database_config(name="minio")
|
||||||
elif STORAGE_IMPL_TYPE == 'OSS':
|
elif STORAGE_IMPL_TYPE == 'OSS':
|
||||||
OSS = get_base_config("oss", {})
|
OSS = get_base_config("oss", {})
|
||||||
|
elif STORAGE_IMPL_TYPE == 'GCS':
|
||||||
|
GCS = get_base_config("gcs", {})
|
||||||
|
|
||||||
global STORAGE_IMPL
|
global STORAGE_IMPL
|
||||||
STORAGE_IMPL = StorageFactory.create(Storage[STORAGE_IMPL_TYPE])
|
STORAGE_IMPL = StorageFactory.create(Storage[STORAGE_IMPL_TYPE])
|
||||||
|
|||||||
@ -60,6 +60,8 @@ user_default_llm:
|
|||||||
# access_key: 'access_key'
|
# access_key: 'access_key'
|
||||||
# secret_key: 'secret_key'
|
# secret_key: 'secret_key'
|
||||||
# region: 'region'
|
# region: 'region'
|
||||||
|
#gcs:
|
||||||
|
# bucket: 'bridgtl-edm-d-bucket-ragflow'
|
||||||
# oss:
|
# oss:
|
||||||
# access_key: 'access_key'
|
# access_key: 'access_key'
|
||||||
# secret_key: 'secret_key'
|
# secret_key: 'secret_key'
|
||||||
|
|||||||
@ -25,6 +25,8 @@ from rag.prompts.generator import vision_llm_figure_describe_prompt
|
|||||||
|
|
||||||
|
|
||||||
def vision_figure_parser_figure_data_wrapper(figures_data_without_positions):
|
def vision_figure_parser_figure_data_wrapper(figures_data_without_positions):
|
||||||
|
if not figures_data_without_positions:
|
||||||
|
return []
|
||||||
return [
|
return [
|
||||||
(
|
(
|
||||||
(figure_data[1], [figure_data[0]]),
|
(figure_data[1], [figure_data[0]]),
|
||||||
@ -35,7 +37,9 @@ def vision_figure_parser_figure_data_wrapper(figures_data_without_positions):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def vision_figure_parser_docx_wrapper(sections,tbls,callback=None,**kwargs):
|
def vision_figure_parser_docx_wrapper(sections, tbls, callback=None,**kwargs):
|
||||||
|
if not tbls:
|
||||||
|
return []
|
||||||
try:
|
try:
|
||||||
vision_model = LLMBundle(kwargs["tenant_id"], LLMType.IMAGE2TEXT)
|
vision_model = LLMBundle(kwargs["tenant_id"], LLMType.IMAGE2TEXT)
|
||||||
callback(0.7, "Visual model detected. Attempting to enhance figure extraction...")
|
callback(0.7, "Visual model detected. Attempting to enhance figure extraction...")
|
||||||
@ -53,6 +57,8 @@ def vision_figure_parser_docx_wrapper(sections,tbls,callback=None,**kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def vision_figure_parser_pdf_wrapper(tbls, callback=None, **kwargs):
|
def vision_figure_parser_pdf_wrapper(tbls, callback=None, **kwargs):
|
||||||
|
if not tbls:
|
||||||
|
return []
|
||||||
try:
|
try:
|
||||||
vision_model = LLMBundle(kwargs["tenant_id"], LLMType.IMAGE2TEXT)
|
vision_model = LLMBundle(kwargs["tenant_id"], LLMType.IMAGE2TEXT)
|
||||||
callback(0.7, "Visual model detected. Attempting to enhance figure extraction...")
|
callback(0.7, "Visual model detected. Attempting to enhance figure extraction...")
|
||||||
|
|||||||
@ -23,7 +23,7 @@ services:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
@ -48,7 +48,7 @@ services:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
|
|||||||
@ -31,7 +31,7 @@ services:
|
|||||||
retries: 120
|
retries: 120
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
opensearch01:
|
opensearch01:
|
||||||
profiles:
|
profiles:
|
||||||
@ -67,12 +67,12 @@ services:
|
|||||||
retries: 120
|
retries: 120
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
infinity:
|
infinity:
|
||||||
profiles:
|
profiles:
|
||||||
- infinity
|
- infinity
|
||||||
image: infiniflow/infinity:v0.6.8
|
image: infiniflow/infinity:v0.6.10
|
||||||
volumes:
|
volumes:
|
||||||
- infinity_data:/var/infinity
|
- infinity_data:/var/infinity
|
||||||
- ./infinity_conf.toml:/infinity_conf.toml
|
- ./infinity_conf.toml:/infinity_conf.toml
|
||||||
@ -94,7 +94,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 120
|
retries: 120
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
oceanbase:
|
oceanbase:
|
||||||
profiles:
|
profiles:
|
||||||
@ -119,7 +119,7 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
sandbox-executor-manager:
|
sandbox-executor-manager:
|
||||||
profiles:
|
profiles:
|
||||||
@ -147,7 +147,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 120
|
retries: 120
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
# mysql:5.7 linux/arm64 image is unavailable.
|
# mysql:5.7 linux/arm64 image is unavailable.
|
||||||
@ -175,7 +175,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 120
|
retries: 120
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z
|
image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z
|
||||||
@ -191,7 +191,7 @@ services:
|
|||||||
- minio_data:/data
|
- minio_data:/data
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
@ -209,7 +209,7 @@ services:
|
|||||||
- redis_data:/data
|
- redis_data:/data
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
@ -228,7 +228,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
command: ["--model-id", "/data/${TEI_MODEL}", "--auto-truncate"]
|
command: ["--model-id", "/data/${TEI_MODEL}", "--auto-truncate"]
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|
||||||
tei-gpu:
|
tei-gpu:
|
||||||
@ -249,7 +249,7 @@ services:
|
|||||||
- driver: nvidia
|
- driver: nvidia
|
||||||
count: all
|
count: all
|
||||||
capabilities: [gpu]
|
capabilities: [gpu]
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
@ -271,7 +271,7 @@ services:
|
|||||||
retries: 120
|
retries: 120
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@ -22,7 +22,7 @@ services:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
# If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
@ -39,7 +39,7 @@ services:
|
|||||||
# entrypoint: "/ragflow/entrypoint_task_executor.sh 1 3"
|
# entrypoint: "/ragflow/entrypoint_task_executor.sh 1 3"
|
||||||
# networks:
|
# networks:
|
||||||
# - ragflow
|
# - ragflow
|
||||||
# restart: on-failure
|
# restart: unless-stopped
|
||||||
# # https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# # https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# # If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
# # If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
||||||
# extra_hosts:
|
# extra_hosts:
|
||||||
|
|||||||
@ -45,7 +45,7 @@ services:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# If you use Docker Desktop, the --add-host flag is optional. This flag ensures that the host's internal IP is exposed to the Prometheus container.
|
# If you use Docker Desktop, the --add-host flag is optional. This flag ensures that the host's internal IP is exposed to the Prometheus container.
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
@ -94,7 +94,7 @@ services:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
networks:
|
networks:
|
||||||
- ragflow
|
- ragflow
|
||||||
restart: on-failure
|
restart: unless-stopped
|
||||||
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# If you use Docker Desktop, the --add-host flag is optional. This flag ensures that the host's internal IP is exposed to the Prometheus container.
|
# If you use Docker Desktop, the --add-host flag is optional. This flag ensures that the host's internal IP is exposed to the Prometheus container.
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
@ -120,7 +120,7 @@ services:
|
|||||||
# entrypoint: "/ragflow/entrypoint_task_executor.sh 1 3"
|
# entrypoint: "/ragflow/entrypoint_task_executor.sh 1 3"
|
||||||
# networks:
|
# networks:
|
||||||
# - ragflow
|
# - ragflow
|
||||||
# restart: on-failure
|
# restart: unless-stopped
|
||||||
# # https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
# # https://docs.docker.com/engine/daemon/prometheus/#create-a-prometheus-configuration
|
||||||
# # If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
# # If you're using Docker Desktop, the --add-host flag is optional. This flag makes sure that the host's internal IP gets exposed to the Prometheus container.
|
||||||
# extra_hosts:
|
# extra_hosts:
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[general]
|
[general]
|
||||||
version = "0.6.8"
|
version = "0.6.10"
|
||||||
time_zone = "utc-8"
|
time_zone = "utc-8"
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
|
|||||||
@ -512,13 +512,16 @@ curl --request POST \
|
|||||||
- Maximum: `2048`
|
- Maximum: `2048`
|
||||||
- `"delimiter"`: `string`
|
- `"delimiter"`: `string`
|
||||||
- Defaults to `"\n"`.
|
- Defaults to `"\n"`.
|
||||||
- `"html4excel"`: `bool` Indicates whether to convert Excel documents into HTML format.
|
- `"html4excel"`: `bool`
|
||||||
|
- Whether to convert Excel documents into HTML format.
|
||||||
- Defaults to `false`
|
- Defaults to `false`
|
||||||
- `"layout_recognize"`: `string`
|
- `"layout_recognize"`: `string`
|
||||||
- Defaults to `DeepDOC`
|
- Defaults to `DeepDOC`
|
||||||
- `"tag_kb_ids"`: `array<string>` refer to [Use tag set](https://ragflow.io/docs/dev/use_tag_sets)
|
- `"tag_kb_ids"`: `array<string>`
|
||||||
- Must include a list of dataset IDs, where each dataset is parsed using the Tag Chunking Method
|
- IDs of datasets to be parsed using the Tag chunk method.
|
||||||
- `"task_page_size"`: `int` For PDF only.
|
- Before setting this, ensure a tag set is created and properly configured. For details, see [Use tag set](https://ragflow.io/docs/dev/use_tag_sets).
|
||||||
|
- `"task_page_size"`: `int`
|
||||||
|
- For PDFs only.
|
||||||
- Defaults to `12`
|
- Defaults to `12`
|
||||||
- Minimum: `1`
|
- Minimum: `1`
|
||||||
- `"raptor"`: `object` RAPTOR-specific settings.
|
- `"raptor"`: `object` RAPTOR-specific settings.
|
||||||
|
|||||||
@ -43,7 +43,6 @@ def get_urls(use_china_mirrors=False) -> list[Union[str, list[str]]]:
|
|||||||
repos = [
|
repos = [
|
||||||
"InfiniFlow/text_concat_xgb_v1.0",
|
"InfiniFlow/text_concat_xgb_v1.0",
|
||||||
"InfiniFlow/deepdoc",
|
"InfiniFlow/deepdoc",
|
||||||
"InfiniFlow/huqie",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ ragflow:
|
|||||||
infinity:
|
infinity:
|
||||||
image:
|
image:
|
||||||
repository: infiniflow/infinity
|
repository: infiniflow/infinity
|
||||||
tag: v0.6.8
|
tag: v0.6.10
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
pullSecrets: []
|
pullSecrets: []
|
||||||
storage:
|
storage:
|
||||||
|
|||||||
@ -49,7 +49,7 @@ dependencies = [
|
|||||||
"html-text==0.6.2",
|
"html-text==0.6.2",
|
||||||
"httpx[socks]>=0.28.1,<0.29.0",
|
"httpx[socks]>=0.28.1,<0.29.0",
|
||||||
"huggingface-hub>=0.25.0,<0.26.0",
|
"huggingface-hub>=0.25.0,<0.26.0",
|
||||||
"infinity-sdk==0.6.8",
|
"infinity-sdk==0.6.10",
|
||||||
"infinity-emb>=0.0.66,<0.0.67",
|
"infinity-emb>=0.0.66,<0.0.67",
|
||||||
"itsdangerous==2.1.2",
|
"itsdangerous==2.1.2",
|
||||||
"json-repair==0.35.0",
|
"json-repair==0.35.0",
|
||||||
|
|||||||
@ -86,9 +86,11 @@ class Pdf(PdfParser):
|
|||||||
|
|
||||||
# (A) Add text
|
# (A) Add text
|
||||||
for b in self.boxes:
|
for b in self.boxes:
|
||||||
if not (from_page < b["page_number"] <= to_page + from_page):
|
# b["page_number"] is relative page number,must + from_page
|
||||||
|
global_page_num = b["page_number"] + from_page
|
||||||
|
if not (from_page < global_page_num <= to_page + from_page):
|
||||||
continue
|
continue
|
||||||
page_items[b["page_number"]].append({
|
page_items[global_page_num].append({
|
||||||
"top": b["top"],
|
"top": b["top"],
|
||||||
"x0": b["x0"],
|
"x0": b["x0"],
|
||||||
"text": b["text"],
|
"text": b["text"],
|
||||||
@ -100,7 +102,6 @@ class Pdf(PdfParser):
|
|||||||
if not positions:
|
if not positions:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Handle content type (list vs str)
|
|
||||||
if isinstance(content, list):
|
if isinstance(content, list):
|
||||||
final_text = "\n".join(content)
|
final_text = "\n".join(content)
|
||||||
elif isinstance(content, str):
|
elif isinstance(content, str):
|
||||||
@ -109,10 +110,11 @@ class Pdf(PdfParser):
|
|||||||
final_text = str(content)
|
final_text = str(content)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Parse positions
|
|
||||||
pn_index = positions[0][0]
|
pn_index = positions[0][0]
|
||||||
if isinstance(pn_index, list):
|
if isinstance(pn_index, list):
|
||||||
pn_index = pn_index[0]
|
pn_index = pn_index[0]
|
||||||
|
|
||||||
|
# pn_index in tbls is absolute page number
|
||||||
current_page_num = int(pn_index) + 1
|
current_page_num = int(pn_index) + 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error parsing position: {e}")
|
print(f"Error parsing position: {e}")
|
||||||
|
|||||||
@ -343,7 +343,8 @@ def form_history(history, limit=-6):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict], user_defined_prompts: dict={}):
|
|
||||||
|
async def analyze_task_async(chat_mdl, prompt, task_name, tools_description: list[dict], user_defined_prompts: dict={}):
|
||||||
tools_desc = tool_schema(tools_description)
|
tools_desc = tool_schema(tools_description)
|
||||||
context = ""
|
context = ""
|
||||||
|
|
||||||
@ -352,7 +353,7 @@ def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict], use
|
|||||||
else:
|
else:
|
||||||
template = PROMPT_JINJA_ENV.from_string(ANALYZE_TASK_SYSTEM + "\n\n" + ANALYZE_TASK_USER)
|
template = PROMPT_JINJA_ENV.from_string(ANALYZE_TASK_SYSTEM + "\n\n" + ANALYZE_TASK_USER)
|
||||||
context = template.render(task=task_name, context=context, agent_prompt=prompt, tools_desc=tools_desc)
|
context = template.render(task=task_name, context=context, agent_prompt=prompt, tools_desc=tools_desc)
|
||||||
kwd = chat_mdl.chat(context, [{"role": "user", "content": "Please analyze it."}])
|
kwd = await _chat_async(chat_mdl, context, [{"role": "user", "content": "Please analyze it."}])
|
||||||
if isinstance(kwd, tuple):
|
if isinstance(kwd, tuple):
|
||||||
kwd = kwd[0]
|
kwd = kwd[0]
|
||||||
kwd = re.sub(r"^.*</think>", "", kwd, flags=re.DOTALL)
|
kwd = re.sub(r"^.*</think>", "", kwd, flags=re.DOTALL)
|
||||||
@ -361,13 +362,17 @@ def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict], use
|
|||||||
return kwd
|
return kwd
|
||||||
|
|
||||||
|
|
||||||
async def analyze_task_async(chat_mdl, prompt, task_name, tools_description: list[dict], user_defined_prompts: dict={}):
|
async def _chat_async(chat_mdl, system: str, history: list, **kwargs):
|
||||||
return await asyncio.to_thread(analyze_task, chat_mdl, prompt, task_name, tools_description, user_defined_prompts)
|
chat_async = getattr(chat_mdl, "async_chat", None)
|
||||||
|
if chat_async and asyncio.iscoroutinefunction(chat_async):
|
||||||
|
return await chat_async(system, history, **kwargs)
|
||||||
|
return await asyncio.to_thread(chat_mdl.chat, system, history, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc, user_defined_prompts: dict={}):
|
|
||||||
|
async def next_step_async(chat_mdl, history:list, tools_description: list[dict], task_desc, user_defined_prompts: dict={}):
|
||||||
if not tools_description:
|
if not tools_description:
|
||||||
return ""
|
return "", 0
|
||||||
desc = tool_schema(tools_description)
|
desc = tool_schema(tools_description)
|
||||||
template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("plan_generation", NEXT_STEP))
|
template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("plan_generation", NEXT_STEP))
|
||||||
user_prompt = "\nWhat's the next tool to call? If ready OR IMPOSSIBLE TO BE READY, then call `complete_task`."
|
user_prompt = "\nWhat's the next tool to call? If ready OR IMPOSSIBLE TO BE READY, then call `complete_task`."
|
||||||
@ -376,18 +381,18 @@ def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc,
|
|||||||
hist[-1]["content"] += user_prompt
|
hist[-1]["content"] += user_prompt
|
||||||
else:
|
else:
|
||||||
hist.append({"role": "user", "content": user_prompt})
|
hist.append({"role": "user", "content": user_prompt})
|
||||||
json_str = chat_mdl.chat(template.render(task_analysis=task_desc, desc=desc, today=datetime.datetime.now().strftime("%Y-%m-%d")),
|
json_str = await _chat_async(
|
||||||
hist[1:], stop=["<|stop|>"])
|
chat_mdl,
|
||||||
|
template.render(task_analysis=task_desc, desc=desc, today=datetime.datetime.now().strftime("%Y-%m-%d")),
|
||||||
|
hist[1:],
|
||||||
|
stop=["<|stop|>"],
|
||||||
|
)
|
||||||
tk_cnt = num_tokens_from_string(json_str)
|
tk_cnt = num_tokens_from_string(json_str)
|
||||||
json_str = re.sub(r"^.*</think>", "", json_str, flags=re.DOTALL)
|
json_str = re.sub(r"^.*</think>", "", json_str, flags=re.DOTALL)
|
||||||
return json_str, tk_cnt
|
return json_str, tk_cnt
|
||||||
|
|
||||||
|
|
||||||
async def next_step_async(chat_mdl, history:list, tools_description: list[dict], task_desc, user_defined_prompts: dict={}):
|
async def reflect_async(chat_mdl, history: list[dict], tool_call_res: list[Tuple], user_defined_prompts: dict={}):
|
||||||
return await asyncio.to_thread(next_step, chat_mdl, history, tools_description, task_desc, user_defined_prompts)
|
|
||||||
|
|
||||||
|
|
||||||
def reflect(chat_mdl, history: list[dict], tool_call_res: list[Tuple], user_defined_prompts: dict={}):
|
|
||||||
tool_calls = [{"name": p[0], "result": p[1]} for p in tool_call_res]
|
tool_calls = [{"name": p[0], "result": p[1]} for p in tool_call_res]
|
||||||
goal = history[1]["content"]
|
goal = history[1]["content"]
|
||||||
template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("reflection", REFLECT))
|
template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("reflection", REFLECT))
|
||||||
@ -398,7 +403,7 @@ def reflect(chat_mdl, history: list[dict], tool_call_res: list[Tuple], user_defi
|
|||||||
else:
|
else:
|
||||||
hist.append({"role": "user", "content": user_prompt})
|
hist.append({"role": "user", "content": user_prompt})
|
||||||
_, msg = message_fit_in(hist, chat_mdl.max_length)
|
_, msg = message_fit_in(hist, chat_mdl.max_length)
|
||||||
ans = chat_mdl.chat(msg[0]["content"], msg[1:])
|
ans = await _chat_async(chat_mdl, msg[0]["content"], msg[1:])
|
||||||
ans = re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
ans = re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
||||||
return """
|
return """
|
||||||
**Observation**
|
**Observation**
|
||||||
@ -429,23 +434,15 @@ def tool_call_summary(chat_mdl, name: str, params: dict, result: str, user_defin
|
|||||||
return re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
return re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
def rank_memories(chat_mdl, goal:str, sub_goal:str, tool_call_summaries: list[str], user_defined_prompts: dict={}):
|
async def rank_memories_async(chat_mdl, goal:str, sub_goal:str, tool_call_summaries: list[str], user_defined_prompts: dict={}):
|
||||||
template = PROMPT_JINJA_ENV.from_string(RANK_MEMORY)
|
template = PROMPT_JINJA_ENV.from_string(RANK_MEMORY)
|
||||||
system_prompt = template.render(goal=goal, sub_goal=sub_goal, results=[{"i": i, "content": s} for i,s in enumerate(tool_call_summaries)])
|
system_prompt = template.render(goal=goal, sub_goal=sub_goal, results=[{"i": i, "content": s} for i,s in enumerate(tool_call_summaries)])
|
||||||
user_prompt = " → rank: "
|
user_prompt = " → rank: "
|
||||||
_, msg = message_fit_in(form_message(system_prompt, user_prompt), chat_mdl.max_length)
|
_, msg = message_fit_in(form_message(system_prompt, user_prompt), chat_mdl.max_length)
|
||||||
ans = chat_mdl.chat(msg[0]["content"], msg[1:], stop="<|stop|>")
|
ans = await _chat_async(chat_mdl, msg[0]["content"], msg[1:], stop="<|stop|>")
|
||||||
return re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
return re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
async def reflect_async(chat_mdl, history: list[dict], tool_call_res: list[Tuple], user_defined_prompts: dict={}):
|
|
||||||
return await asyncio.to_thread(reflect, chat_mdl, history, tool_call_res, user_defined_prompts)
|
|
||||||
|
|
||||||
|
|
||||||
async def rank_memories_async(chat_mdl, goal:str, sub_goal:str, tool_call_summaries: list[str], user_defined_prompts: dict={}):
|
|
||||||
return await asyncio.to_thread(rank_memories, chat_mdl, goal, sub_goal, tool_call_summaries, user_defined_prompts)
|
|
||||||
|
|
||||||
|
|
||||||
def gen_meta_filter(chat_mdl, meta_data:dict, query: str) -> dict:
|
def gen_meta_filter(chat_mdl, meta_data:dict, query: str) -> dict:
|
||||||
meta_data_structure = {}
|
meta_data_structure = {}
|
||||||
for key, values in meta_data.items():
|
for key, values in meta_data.items():
|
||||||
@ -514,7 +511,7 @@ def toc_index_extractor(toc:list[dict], content:str, chat_mdl):
|
|||||||
|
|
||||||
The structure variable is the numeric system which represents the index of the hierarchy section in the table of contents. For example, the first section has structure index 1, the first subsection has structure index 1.1, the second subsection has structure index 1.2, etc.
|
The structure variable is the numeric system which represents the index of the hierarchy section in the table of contents. For example, the first section has structure index 1, the first subsection has structure index 1.1, the second subsection has structure index 1.2, etc.
|
||||||
|
|
||||||
The response should be in the following JSON format:
|
The response should be in the following JSON format:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"structure": <structure index, "x.x.x" or None> (string),
|
"structure": <structure index, "x.x.x" or None> (string),
|
||||||
@ -641,8 +638,8 @@ def toc_transformer(toc_pages, chat_mdl):
|
|||||||
|
|
||||||
The `structure` is the numeric system which represents the index of the hierarchy section in the table of contents. For example, the first section has structure index 1, the first subsection has structure index 1.1, the second subsection has structure index 1.2, etc.
|
The `structure` is the numeric system which represents the index of the hierarchy section in the table of contents. For example, the first section has structure index 1, the first subsection has structure index 1.1, the second subsection has structure index 1.2, etc.
|
||||||
The `title` is a short phrase or a several-words term.
|
The `title` is a short phrase or a several-words term.
|
||||||
|
|
||||||
The response should be in the following JSON format:
|
The response should be in the following JSON format:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"structure": <structure index, "x.x.x" or None> (string),
|
"structure": <structure index, "x.x.x" or None> (string),
|
||||||
@ -667,7 +664,7 @@ def toc_transformer(toc_pages, chat_mdl):
|
|||||||
while not (if_complete == "yes"):
|
while not (if_complete == "yes"):
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
Your task is to continue the table of contents json structure, directly output the remaining part of the json structure.
|
Your task is to continue the table of contents json structure, directly output the remaining part of the json structure.
|
||||||
The response should be in the following JSON format:
|
The response should be in the following JSON format:
|
||||||
|
|
||||||
The raw table of contents json structure is:
|
The raw table of contents json structure is:
|
||||||
{toc_content}
|
{toc_content}
|
||||||
@ -756,7 +753,7 @@ async def run_toc_from_text(chunks, chat_mdl, callback=None):
|
|||||||
|
|
||||||
for chunk in chunks_res:
|
for chunk in chunks_res:
|
||||||
titles.extend(chunk.get("toc", []))
|
titles.extend(chunk.get("toc", []))
|
||||||
|
|
||||||
# Filter out entries with title == -1
|
# Filter out entries with title == -1
|
||||||
prune = len(titles) > 512
|
prune = len(titles) > 512
|
||||||
max_len = 12 if prune else 22
|
max_len = 12 if prune else 22
|
||||||
|
|||||||
555629
rag/res/huqie.txt
555629
rag/res/huqie.txt
File diff suppressed because it is too large
Load Diff
207
rag/utils/gcs_conn.py
Normal file
207
rag/utils/gcs_conn.py
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
from io import BytesIO
|
||||||
|
from google.cloud import storage
|
||||||
|
from google.api_core.exceptions import NotFound
|
||||||
|
from common.decorator import singleton
|
||||||
|
from common import settings
|
||||||
|
|
||||||
|
|
||||||
|
@singleton
|
||||||
|
class RAGFlowGCS:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = None
|
||||||
|
self.bucket_name = None
|
||||||
|
self.__open__()
|
||||||
|
|
||||||
|
def __open__(self):
|
||||||
|
try:
|
||||||
|
if self.client:
|
||||||
|
self.client = None
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.client = storage.Client()
|
||||||
|
self.bucket_name = settings.GCS["bucket"]
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Fail to connect to GCS")
|
||||||
|
|
||||||
|
def _get_blob_path(self, folder, filename):
|
||||||
|
"""Helper to construct the path: folder/filename"""
|
||||||
|
if not folder:
|
||||||
|
return filename
|
||||||
|
return f"{folder}/{filename}"
|
||||||
|
|
||||||
|
def health(self):
|
||||||
|
folder, fnm, binary = "ragflow-health", "health_check", b"_t@@@1"
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
if not bucket_obj.exists():
|
||||||
|
logging.error(f"Health check failed: Main bucket '{self.bucket_name}' does not exist.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
blob_path = self._get_blob_path(folder, fnm)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
blob.upload_from_file(BytesIO(binary), content_type='application/octet-stream')
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception(f"Health check failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def put(self, bucket, fnm, binary, tenant_id=None):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket (to match interface)
|
||||||
|
for _ in range(3):
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
blob_path = self._get_blob_path(bucket, fnm)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
|
||||||
|
blob.upload_from_file(BytesIO(binary), content_type='application/octet-stream')
|
||||||
|
return True
|
||||||
|
except NotFound:
|
||||||
|
logging.error(f"Fail to put: Main bucket {self.bucket_name} does not exist.")
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to put {bucket}/{fnm}:")
|
||||||
|
self.__open__()
|
||||||
|
time.sleep(1)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def rm(self, bucket, fnm, tenant_id=None):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
blob_path = self._get_blob_path(bucket, fnm)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
blob.delete()
|
||||||
|
except NotFound:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to remove {bucket}/{fnm}:")
|
||||||
|
|
||||||
|
def get(self, bucket, filename, tenant_id=None):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
for _ in range(1):
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
blob_path = self._get_blob_path(bucket, filename)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
return blob.download_as_bytes()
|
||||||
|
except NotFound:
|
||||||
|
logging.warning(f"File not found {bucket}/{filename} in {self.bucket_name}")
|
||||||
|
return None
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to get {bucket}/{filename}")
|
||||||
|
self.__open__()
|
||||||
|
time.sleep(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def obj_exist(self, bucket, filename, tenant_id=None):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
blob_path = self._get_blob_path(bucket, filename)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
return blob.exists()
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"obj_exist {bucket}/{filename} got exception")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def bucket_exists(self, bucket):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
return bucket_obj.exists()
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"bucket_exist check for {self.bucket_name} got exception")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_presigned_url(self, bucket, fnm, expires, tenant_id=None):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
for _ in range(10):
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
blob_path = self._get_blob_path(bucket, fnm)
|
||||||
|
blob = bucket_obj.blob(blob_path)
|
||||||
|
|
||||||
|
expiration = expires
|
||||||
|
if isinstance(expires, int):
|
||||||
|
expiration = datetime.timedelta(seconds=expires)
|
||||||
|
|
||||||
|
url = blob.generate_signed_url(
|
||||||
|
version="v4",
|
||||||
|
expiration=expiration,
|
||||||
|
method="GET"
|
||||||
|
)
|
||||||
|
return url
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to get_presigned {bucket}/{fnm}:")
|
||||||
|
self.__open__()
|
||||||
|
time.sleep(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def remove_bucket(self, bucket):
|
||||||
|
# RENAMED PARAMETER: bucket_name -> bucket
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
prefix = f"{bucket}/"
|
||||||
|
|
||||||
|
blobs = list(self.client.list_blobs(self.bucket_name, prefix=prefix))
|
||||||
|
|
||||||
|
if blobs:
|
||||||
|
bucket_obj.delete_blobs(blobs)
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to remove virtual bucket (folder) {bucket}")
|
||||||
|
|
||||||
|
def copy(self, src_bucket, src_path, dest_bucket, dest_path):
|
||||||
|
# RENAMED PARAMETERS to match original interface
|
||||||
|
try:
|
||||||
|
bucket_obj = self.client.bucket(self.bucket_name)
|
||||||
|
|
||||||
|
src_blob_path = self._get_blob_path(src_bucket, src_path)
|
||||||
|
dest_blob_path = self._get_blob_path(dest_bucket, dest_path)
|
||||||
|
|
||||||
|
src_blob = bucket_obj.blob(src_blob_path)
|
||||||
|
|
||||||
|
if not src_blob.exists():
|
||||||
|
logging.error(f"Source object not found: {src_blob_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
bucket_obj.copy_blob(src_blob, bucket_obj, dest_blob_path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except NotFound:
|
||||||
|
logging.error(f"Copy failed: Main bucket {self.bucket_name} does not exist.")
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to copy {src_bucket}/{src_path} -> {dest_bucket}/{dest_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def move(self, src_bucket, src_path, dest_bucket, dest_path):
|
||||||
|
try:
|
||||||
|
if self.copy(src_bucket, src_path, dest_bucket, dest_path):
|
||||||
|
self.rm(src_bucket, src_path)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logging.error(f"Copy failed, move aborted: {src_bucket}/{src_path}")
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Fail to move {src_bucket}/{src_path} -> {dest_bucket}/{dest_path}")
|
||||||
|
return False
|
||||||
163
uv.lock
generated
163
uv.lock
generated
@ -445,25 +445,25 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bce-python-sdk"
|
name = "bce-python-sdk"
|
||||||
version = "0.9.54"
|
version = "0.9.55"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "future" },
|
{ name = "future" },
|
||||||
{ name = "pycryptodome" },
|
{ name = "pycryptodome" },
|
||||||
{ name = "six" },
|
{ name = "six" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/c8/1c3bc30aa745ad4c3d073f150bddaf1d43ee6ee33f0b8ec60068494f511e/bce_python_sdk-0.9.54.tar.gz", hash = "sha256:f68026f40f11ea38ef445f50a7756009d5b703c7253438b138b30fb3b83be275", size = 275698, upload-time = "2025-11-27T02:28:50.24Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/ae/f31ee3ccae94e1a07d8886a413f08c1581349e6cb45bf8b3c608fbf173e4/bce_python_sdk-0.9.55.tar.gz", hash = "sha256:bed63f8a0975f2e9daecf53417c3d5b803232ad87f35a0b16e25850710ce209c", size = 275733, upload-time = "2025-12-02T12:02:38.041Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/a7/b8806c8505bb830cc863837ef8b42695170dd9561605c61262250df066d3/bce_python_sdk-0.9.54-py3-none-any.whl", hash = "sha256:a084eee577931f15a55280a7401bea2474115989ee79ebbca131610bdce81c99", size = 390447, upload-time = "2025-11-27T02:28:48.603Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/01/1b13a627e5f0239f24b168138d9a948e876d4b387c03f59d31699578c960/bce_python_sdk-0.9.55-py3-none-any.whl", hash = "sha256:6045d19d783b548644cce50a2f41ef5242da6654fb91b2c21629f309ca6dbf4c", size = 390463, upload-time = "2025-12-02T12:02:36.417Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beartype"
|
name = "beartype"
|
||||||
version = "0.22.7"
|
version = "0.22.8"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/49/e28a77f8a3868b1c9ff6a030678e84de24c4783bae4c12cec9443cf8fb54/beartype-0.22.7.tar.gz", hash = "sha256:c7269855b71e32b7c9f0fc662baade752eb525107266e053338c2f6e8873826b", size = 1599627, upload-time = "2025-11-29T06:49:56.751Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/1d/794ae2acaa67c8b216d91d5919da2606c2bb14086849ffde7f5555f3a3a5/beartype-0.22.8.tar.gz", hash = "sha256:b19b21c9359722ee3f7cc433f063b3e13997b27ae8226551ea5062e621f61165", size = 1602262, upload-time = "2025-12-03T05:11:10.766Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/0c/a764253610513295b7f57904b91fae1d99c7afd1b16b6eaae06fdfb71fb5/beartype-0.22.7-py3-none-any.whl", hash = "sha256:e13430ac07c61fa4bc54d375970438aeb9aa47a482c529a6f438ce52e18e6f50", size = 1330771, upload-time = "2025-11-29T06:49:54.545Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/2a/fbcbf5a025d3e71ddafad7efd43e34ec4362f4d523c3c471b457148fb211/beartype-0.22.8-py3-none-any.whl", hash = "sha256:b832882d04e41a4097bab9f63e6992bc6de58c414ee84cba9b45b67314f5ab2e", size = 1331895, upload-time = "2025-12-03T05:11:08.373Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1910,11 +1910,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsspec"
|
name = "fsspec"
|
||||||
version = "2025.10.0"
|
version = "2025.12.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/27/954057b0d1f53f086f681755207dda6de6c660ce133c829158e8e8fe7895/fsspec-2025.12.0.tar.gz", hash = "sha256:c505de011584597b1060ff778bb664c1bc022e87921b0e4f10cc9c44f9635973", size = 309748, upload-time = "2025-12-03T15:23:42.687Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/c7/b64cae5dba3a1b138d7123ec36bb5ccd39d39939f18454407e5468f4763f/fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b", size = 201422, upload-time = "2025-12-03T15:23:41.434Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2022,16 +2022,21 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-auth"
|
name = "google-auth"
|
||||||
version = "2.41.1"
|
version = "2.43.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cachetools" },
|
{ name = "cachetools" },
|
||||||
{ name = "pyasn1-modules" },
|
{ name = "pyasn1-modules" },
|
||||||
{ name = "rsa" },
|
{ name = "rsa" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/af/5129ce5b2f9688d2fa49b463e544972a7c82b0fdb50980dafee92e121d9f/google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2", size = 292284, upload-time = "2025-09-30T22:51:26.363Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359, upload-time = "2025-11-06T00:13:36.587Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/a4/7319a2a8add4cc352be9e3efeff5e2aacee917c85ca2fa1647e29089983c/google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d", size = 221302, upload-time = "2025-09-30T22:51:24.212Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114, upload-time = "2025-11-06T00:13:35.209Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
requests = [
|
||||||
|
{ name = "requests" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2049,15 +2054,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-auth-oauthlib"
|
name = "google-auth-oauthlib"
|
||||||
version = "1.2.3"
|
version = "1.2.2"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "google-auth" },
|
{ name = "google-auth" },
|
||||||
{ name = "requests-oauthlib" },
|
{ name = "requests-oauthlib" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/a6/c6336a6ceb682709a4aa39e2e6b5754a458075ca92359512b6cbfcb25ae3/google_auth_oauthlib-1.2.3.tar.gz", hash = "sha256:eb09e450d3cc789ecbc2b3529cb94a713673fd5f7a22c718ad91cf75aedc2ea4", size = 21265, upload-time = "2025-10-30T21:28:19.105Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/87/e10bf24f7bcffc1421b84d6f9c3377c30ec305d082cd737ddaa6d8f77f7c/google_auth_oauthlib-1.2.2.tar.gz", hash = "sha256:11046fb8d3348b296302dd939ace8af0a724042e8029c1b872d87fabc9f41684", size = 20955, upload-time = "2025-04-22T16:40:29.172Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/07/a54c100da461ffc5968457823fcc665a48fb4b875c68bcfecbfe24a10dbe/google_auth_oauthlib-1.2.3-py3-none-any.whl", hash = "sha256:7c0940e037677f25e71999607493640d071212e7f3c15aa0febea4c47a5a0680", size = 19184, upload-time = "2025-10-30T21:28:17.88Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/84/40ee070be95771acd2f4418981edb834979424565c3eec3cd88b6aa09d24/google_auth_oauthlib-1.2.2-py3-none-any.whl", hash = "sha256:fd619506f4b3908b5df17b65f39ca8d66ea56986e5472eb5978fd8f3786f00a2", size = 19072, upload-time = "2025-04-22T16:40:28.174Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2177,11 +2182,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-genai"
|
name = "google-genai"
|
||||||
version = "1.52.0"
|
version = "1.53.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
{ name = "google-auth" },
|
{ name = "google-auth", extra = ["requests"] },
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
@ -2189,9 +2194,9 @@ dependencies = [
|
|||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
{ name = "websockets" },
|
{ name = "websockets" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/4e/0ad8585d05312074bb69711b2d81cfed69ce0ae441913d57bf169bed20a7/google_genai-1.52.0.tar.gz", hash = "sha256:a74e8a4b3025f23aa98d6a0f84783119012ca6c336fd68f73c5d2b11465d7fc5", size = 258743, upload-time = "2025-11-21T02:18:55.742Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/b3/36fbfde2e21e6d3bc67780b61da33632f495ab1be08076cf0a16af74098f/google_genai-1.53.0.tar.gz", hash = "sha256:938a26d22f3fd32c6eeeb4276ef204ef82884e63af9842ce3eac05ceb39cbd8d", size = 260102, upload-time = "2025-12-03T17:21:23.233Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/66/03f663e7bca7abe9ccfebe6cb3fe7da9a118fd723a5abb278d6117e7990e/google_genai-1.52.0-py3-none-any.whl", hash = "sha256:c8352b9f065ae14b9322b949c7debab8562982f03bf71d44130cd2b798c20743", size = 261219, upload-time = "2025-11-21T02:18:54.515Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/f2/97fefdd1ad1f3428321bac819ae7a83ccc59f6439616054736b7819fa56c/google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094", size = 262015, upload-time = "2025-12-03T17:21:21.934Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2776,7 +2781,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "infinity-sdk"
|
name = "infinity-sdk"
|
||||||
version = "0.6.8"
|
version = "0.6.10"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "datrie" },
|
{ name = "datrie" },
|
||||||
@ -2795,9 +2800,9 @@ dependencies = [
|
|||||||
{ name = "sqlglot", extra = ["rs"] },
|
{ name = "sqlglot", extra = ["rs"] },
|
||||||
{ name = "thrift" },
|
{ name = "thrift" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/2d/4b699d62202319e5cbbcb4a7d9e87a86dde7ba7c767d0af4ebbee3de8419/infinity_sdk-0.6.8.tar.gz", hash = "sha256:e91c1f6cdf2fa41bc615c72be2a9e981211bd05b34522c1d27f1b825b905b125", size = 72669, upload-time = "2025-12-02T05:09:29.377Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/e5/88fdcfe42835c5494a08f02b64762a98e04dae4ad49f7dfabac18ee01928/infinity_sdk-0.6.10.tar.gz", hash = "sha256:b55c296ca3b2c8c2f4568f359dd8a50772e9432f09b64667140e9804bf780436", size = 29502969, upload-time = "2025-12-04T02:42:17.882Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/08/59ed1261ee80d3b2c5a80313a013a94cae83ce90ff1da1ef488055944a7b/infinity_sdk-0.6.8-py3-none-any.whl", hash = "sha256:392f942a2073a5b545261dad9859b217c6a0331ede606c8894e7ae335f2ead5e", size = 81564, upload-time = "2025-12-02T05:09:27.784Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/99/8857ea0805bd83fe092f5dca914a31f9fcc731c3800264657bd3ba950a1d/infinity_sdk-0.6.10-py3-none-any.whl", hash = "sha256:8f605039ec73d1b05d219105fbabef186e0178fddbad058c2c06c4873be48651", size = 29722107, upload-time = "2025-12-04T02:42:04.101Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3077,7 +3082,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langfuse"
|
name = "langfuse"
|
||||||
version = "3.10.3"
|
version = "3.10.5"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "backoff" },
|
{ name = "backoff" },
|
||||||
@ -3091,9 +3096,9 @@ dependencies = [
|
|||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "wrapt" },
|
{ name = "wrapt" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/03/c4316cb0a91cff97118c21b973b3089c2fe1bdbcad02f3623d6ac572e954/langfuse-3.10.3.tar.gz", hash = "sha256:69d6eaf573212f8cdc1cebd2d6b47f271bfe76c7eb5a3c5d6766bb0d9bf0004c", size = 226617, upload-time = "2025-12-01T18:01:02.607Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/21/dff0434290512484436bfa108e36f0adc3457eb4117767de70e76a411cac/langfuse-3.10.5.tar.gz", hash = "sha256:14eb767663f7e7480cd1cd1b3ca457022817c129e666efe97e5c80adb8c5aac0", size = 223142, upload-time = "2025-12-03T17:49:39.747Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/04/f07c2a23f2822f73f8576b1ba7348c014c4be65127384b4bee475f913f3b/langfuse-3.10.3-py3-none-any.whl", hash = "sha256:b9a2e6506f8f0923c2f4b8c9e3fa355231994197a17f75509a37f335660ce334", size = 399062, upload-time = "2025-12-01T18:01:00.688Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/6f/dc15775f82d38da62cd2015110f5802bb175a9ee731a4533fe2a0cdf75b6/langfuse-3.10.5-py3-none-any.whl", hash = "sha256:0223a64109a4293b9bd9b2e0e3229f53b75291cd96341e42cc3eba186973fcdb", size = 398888, upload-time = "2025-12-03T17:49:38.171Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4043,32 +4048,32 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-api"
|
name = "opentelemetry-api"
|
||||||
version = "1.38.0"
|
version = "1.39.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "importlib-metadata" },
|
{ name = "importlib-metadata" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/d8/0f354c375628e048bd0570645b310797299754730079853095bf000fba69/opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12", size = 65242, upload-time = "2025-10-16T08:35:50.25Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0b/e5428c009d4d9af0515b0a8371a8aaae695371af291f45e702f7969dce6b/opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9", size = 65763, upload-time = "2025-12-03T13:19:56.378Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/a2/d86e01c28300bd41bab8f18afd613676e2bd63515417b77636fc1add426f/opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582", size = 65947, upload-time = "2025-10-16T08:35:30.23Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/85/d831a9bc0a9e0e1a304ff3d12c1489a5fbc9bf6690a15dcbdae372bbca45/opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459", size = 66357, upload-time = "2025-12-03T13:19:33.043Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-exporter-otlp-proto-common"
|
name = "opentelemetry-exporter-otlp-proto-common"
|
||||||
version = "1.38.0"
|
version = "1.39.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "opentelemetry-proto" },
|
{ name = "opentelemetry-proto" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/83/dd4660f2956ff88ed071e9e0e36e830df14b8c5dc06722dbde1841accbe8/opentelemetry_exporter_otlp_proto_common-1.38.0.tar.gz", hash = "sha256:e333278afab4695aa8114eeb7bf4e44e65c6607d54968271a249c180b2cb605c", size = 20431, upload-time = "2025-10-16T08:35:53.285Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/cb/3a29ce606b10c76d413d6edd42d25a654af03e73e50696611e757d2602f3/opentelemetry_exporter_otlp_proto_common-1.39.0.tar.gz", hash = "sha256:a135fceed1a6d767f75be65bd2845da344dd8b9258eeed6bc48509d02b184409", size = 20407, upload-time = "2025-12-03T13:19:59.003Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/9e/55a41c9601191e8cd8eb626b54ee6827b9c9d4a46d736f32abc80d8039fc/opentelemetry_exporter_otlp_proto_common-1.38.0-py3-none-any.whl", hash = "sha256:03cb76ab213300fe4f4c62b7d8f17d97fcfd21b89f0b5ce38ea156327ddda74a", size = 18359, upload-time = "2025-10-16T08:35:34.099Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/c6/215edba62d13a3948c718b289539f70e40965bc37fc82ecd55bb0b749c1a/opentelemetry_exporter_otlp_proto_common-1.39.0-py3-none-any.whl", hash = "sha256:3d77be7c4bdf90f1a76666c934368b8abed730b5c6f0547a2ec57feb115849ac", size = 18367, upload-time = "2025-12-03T13:19:36.906Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-exporter-otlp-proto-http"
|
name = "opentelemetry-exporter-otlp-proto-http"
|
||||||
version = "1.38.0"
|
version = "1.39.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "googleapis-common-protos" },
|
{ name = "googleapis-common-protos" },
|
||||||
@ -4079,48 +4084,48 @@ dependencies = [
|
|||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/0a/debcdfb029fbd1ccd1563f7c287b89a6f7bef3b2902ade56797bfd020854/opentelemetry_exporter_otlp_proto_http-1.38.0.tar.gz", hash = "sha256:f16bd44baf15cbe07633c5112ffc68229d0edbeac7b37610be0b2def4e21e90b", size = 17282, upload-time = "2025-10-16T08:35:54.422Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/dc/1e9bf3f6a28e29eba516bc0266e052996d02bc7e92675f3cd38169607609/opentelemetry_exporter_otlp_proto_http-1.39.0.tar.gz", hash = "sha256:28d78fc0eb82d5a71ae552263d5012fa3ebad18dfd189bf8d8095ba0e65ee1ed", size = 17287, upload-time = "2025-12-03T13:20:01.134Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/77/154004c99fb9f291f74aa0822a2f5bbf565a72d8126b3a1b63ed8e5f83c7/opentelemetry_exporter_otlp_proto_http-1.38.0-py3-none-any.whl", hash = "sha256:84b937305edfc563f08ec69b9cb2298be8188371217e867c1854d77198d0825b", size = 19579, upload-time = "2025-10-16T08:35:36.269Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/46/e4a102e17205bb05a50dbf24ef0e92b66b648cd67db9a68865af06a242fd/opentelemetry_exporter_otlp_proto_http-1.39.0-py3-none-any.whl", hash = "sha256:5789cb1375a8b82653328c0ce13a054d285f774099faf9d068032a49de4c7862", size = 19639, upload-time = "2025-12-03T13:19:39.536Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-proto"
|
name = "opentelemetry-proto"
|
||||||
version = "1.38.0"
|
version = "1.39.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "protobuf" },
|
{ name = "protobuf" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/14/f0c4f0f6371b9cb7f9fa9ee8918bfd59ac7040c7791f1e6da32a1839780d/opentelemetry_proto-1.38.0.tar.gz", hash = "sha256:88b161e89d9d372ce723da289b7da74c3a8354a8e5359992be813942969ed468", size = 46152, upload-time = "2025-10-16T08:36:01.612Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/b5/64d2f8c3393cd13ea2092106118f7b98461ba09333d40179a31444c6f176/opentelemetry_proto-1.39.0.tar.gz", hash = "sha256:c1fa48678ad1a1624258698e59be73f990b7fc1f39e73e16a9d08eef65dd838c", size = 46153, upload-time = "2025-12-03T13:20:08.729Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/6a/82b68b14efca5150b2632f3692d627afa76b77378c4999f2648979409528/opentelemetry_proto-1.38.0-py3-none-any.whl", hash = "sha256:b6ebe54d3217c42e45462e2a1ae28c3e2bf2ec5a5645236a490f55f45f1a0a18", size = 72535, upload-time = "2025-10-16T08:35:45.749Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/4d/d500e1862beed68318705732d1976c390f4a72ca8009c4983ff627acff20/opentelemetry_proto-1.39.0-py3-none-any.whl", hash = "sha256:1e086552ac79acb501485ff0ce75533f70f3382d43d0a30728eeee594f7bf818", size = 72534, upload-time = "2025-12-03T13:19:50.251Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-sdk"
|
name = "opentelemetry-sdk"
|
||||||
version = "1.38.0"
|
version = "1.39.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "opentelemetry-api" },
|
{ name = "opentelemetry-api" },
|
||||||
{ name = "opentelemetry-semantic-conventions" },
|
{ name = "opentelemetry-semantic-conventions" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/cb/f0eee1445161faf4c9af3ba7b848cc22a50a3d3e2515051ad8628c35ff80/opentelemetry_sdk-1.38.0.tar.gz", hash = "sha256:93df5d4d871ed09cb4272305be4d996236eedb232253e3ab864c8620f051cebe", size = 171942, upload-time = "2025-10-16T08:36:02.257Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/e3/7cd989003e7cde72e0becfe830abff0df55c69d237ee7961a541e0167833/opentelemetry_sdk-1.39.0.tar.gz", hash = "sha256:c22204f12a0529e07aa4d985f1bca9d6b0e7b29fe7f03e923548ae52e0e15dde", size = 171322, upload-time = "2025-12-03T13:20:09.651Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/2e/e93777a95d7d9c40d270a371392b6d6f1ff170c2a3cb32d6176741b5b723/opentelemetry_sdk-1.38.0-py3-none-any.whl", hash = "sha256:1c66af6564ecc1553d72d811a01df063ff097cdc82ce188da9951f93b8d10f6b", size = 132349, upload-time = "2025-10-16T08:35:46.995Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/b4/2adc8bc83eb1055ecb592708efb6f0c520cc2eb68970b02b0f6ecda149cf/opentelemetry_sdk-1.39.0-py3-none-any.whl", hash = "sha256:90cfb07600dfc0d2de26120cebc0c8f27e69bf77cd80ef96645232372709a514", size = 132413, upload-time = "2025-12-03T13:19:51.364Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-semantic-conventions"
|
name = "opentelemetry-semantic-conventions"
|
||||||
version = "0.59b0"
|
version = "0.60b0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "opentelemetry-api" },
|
{ name = "opentelemetry-api" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/bc/8b9ad3802cd8ac6583a4eb7de7e5d7db004e89cb7efe7008f9c8a537ee75/opentelemetry_semantic_conventions-0.59b0.tar.gz", hash = "sha256:7a6db3f30d70202d5bf9fa4b69bc866ca6a30437287de6c510fb594878aed6b0", size = 129861, upload-time = "2025-10-16T08:36:03.346Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/0e/176a7844fe4e3cb5de604212094dffaed4e18b32f1c56b5258bcbcba85c2/opentelemetry_semantic_conventions-0.60b0.tar.gz", hash = "sha256:227d7aa73cbb8a2e418029d6b6465553aa01cf7e78ec9d0bc3255c7b3ac5bf8f", size = 137935, upload-time = "2025-12-03T13:20:12.395Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/7d/c88d7b15ba8fe5c6b8f93be50fc11795e9fc05386c44afaf6b76fe191f9b/opentelemetry_semantic_conventions-0.59b0-py3-none-any.whl", hash = "sha256:35d3b8833ef97d614136e253c1da9342b4c3c083bbaf29ce31d572a1c3825eed", size = 207954, upload-time = "2025-10-16T08:35:48.054Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/56/af0306666f91bae47db14d620775604688361f0f76a872e0005277311131/opentelemetry_semantic_conventions-0.60b0-py3-none-any.whl", hash = "sha256:069530852691136018087b52688857d97bba61cd641d0f8628d2d92788c4f78a", size = 219981, upload-time = "2025-12-03T13:19:53.585Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5683,7 +5688,7 @@ requires-dist = [
|
|||||||
{ name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" },
|
{ name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" },
|
||||||
{ name = "imageio-ffmpeg", specifier = ">=0.6.0" },
|
{ name = "imageio-ffmpeg", specifier = ">=0.6.0" },
|
||||||
{ name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" },
|
{ name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" },
|
||||||
{ name = "infinity-sdk", specifier = "==0.6.8" },
|
{ name = "infinity-sdk", specifier = "==0.6.10" },
|
||||||
{ name = "itsdangerous", specifier = "==2.1.2" },
|
{ name = "itsdangerous", specifier = "==2.1.2" },
|
||||||
{ name = "jira", specifier = "==3.10.5" },
|
{ name = "jira", specifier = "==3.10.5" },
|
||||||
{ name = "json-repair", specifier = "==0.35.0" },
|
{ name = "json-repair", specifier = "==0.35.0" },
|
||||||
@ -6712,11 +6717,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlglot"
|
name = "sqlglot"
|
||||||
version = "28.0.0"
|
version = "28.1.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/8d/9ce5904aca760b81adf821c77a1dcf07c98f9caaa7e3b5c991c541ff89d2/sqlglot-28.0.0.tar.gz", hash = "sha256:cc9a651ef4182e61dac58aa955e5fb21845a5865c6a4d7d7b5a7857450285ad4", size = 5520798, upload-time = "2025-11-17T10:34:57.016Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/49/cda1fc4e610ed5764de2842bb2f362f4aba267b4a7d05a3a217a25b39004/sqlglot-28.1.0.tar.gz", hash = "sha256:a3ef7344359667b51cf95e840aac70a49f847602c61c9fbaeb847f74f7877fe1", size = 5546281, upload-time = "2025-12-02T16:52:28.387Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/6d/86de134f40199105d2fee1b066741aa870b3ce75ee74018d9c8508bbb182/sqlglot-28.0.0-py3-none-any.whl", hash = "sha256:ac1778e7fa4812f4f7e5881b260632fc167b00ca4c1226868891fb15467122e4", size = 536127, upload-time = "2025-11-17T10:34:55.192Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/e8/bd016214348f65ba31107c1b81af70fc7662d96758052d5d59b516fd3858/sqlglot-28.1.0-py3-none-any.whl", hash = "sha256:2a895a31666ba947c686caa980624c82bcd0e6fdf59b4fdb9e47108bd092d1ac", size = 547889, upload-time = "2025-12-02T16:52:26.019Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
@ -6726,40 +6731,40 @@ rs = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlglotrs"
|
name = "sqlglotrs"
|
||||||
version = "0.7.3"
|
version = "0.8.0"
|
||||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/5a/46d8efeda45be6ce1c630229455f000cafedea6129b47e6cfab39ff462f5/sqlglotrs-0.7.3.tar.gz", hash = "sha256:caadc572c8a194f99d6ba44d02f9ada0110e3d47cca3330c81f4aa608f1143eb", size = 15888, upload-time = "2025-10-13T06:33:57.322Z" }
|
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/37/118f24c367fde662e6c1181327dc9c16d08914108904c69bac3a6ba12c52/sqlglotrs-0.8.0.tar.gz", hash = "sha256:2b9a23c580d82be2388ee23496230cfc667f280ed0ed7eaa099d0da8d718cbf2", size = 15706, upload-time = "2025-12-02T16:58:38.197Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/95/f08e01f54e521a286fcd9f7a8bdd178eabcddd9dbc6d6c15dc983c7be8dd/sqlglotrs-0.7.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7acc6dba37af53d9cf1e3217fdd719878dbfaaf2a578ad7b3fbc07ef9dadd035", size = 314621, upload-time = "2025-10-13T06:33:48.917Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/88/7fc59c146118603e06abf69dc19c237ef496a8dd936e5c224fdffc7df120/sqlglotrs-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3db8f75b8efe5b94ed5540c13b80ef0a3e64c0d15864b05a6bccf5554c6e6008", size = 318097, upload-time = "2025-12-02T16:58:30.763Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/7d/01a5db15e413ab587816448f1222286d3a10f0465954d21f5d2915aaeed5/sqlglotrs-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3cbfb42071422afbd7376d70b93a969e86fb74752efe98dd66ee6d2ae27a9665", size = 300189, upload-time = "2025-10-13T06:33:40.963Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/9a/7c0103f02b371f49f6ade420519d54c11c7e3ae4dcf22a855b9c71ccb546/sqlglotrs-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37d00b69814fdabd4256be955d66e699afa1c50740f03369503d85f90245af35", size = 306820, upload-time = "2025-12-02T16:58:23.714Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/21/94d1fb647a394afcb09a9174f7bff078452bb956e6898093dd9ee459ef2b/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07500421de9dea8dfc0cd6769145df754178fc2ae5a3692bdbf5d37aebc0712a", size = 332771, upload-time = "2025-10-13T06:32:45.992Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/cf/52de2a02a52976dfbd863ec57a3fafaf018a9536114f195404d51717501d/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:631da494550442ec2c7139993f59d854e4d4a44282b568594b5fc50818bc4736", size = 341540, upload-time = "2025-12-02T16:57:33.009Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/d1/ccade8e794304c925e9b94e1d7bff4c56896f571a291a03bfd96048c4a0f/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:792eb179a742d7d72d1d47c9a50e073078f0133e9191bd07920945dcc9170844", size = 342960, upload-time = "2025-10-13T06:32:55.493Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/89/072a295c3b98322a3d08d85ed47551c1f080309f2cde2d2fa75bd1964621/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b624e0650067cc006d8a0595e07be3ac91599187ee353313eb9f114ca434e44", size = 350048, upload-time = "2025-12-02T16:57:41.477Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/2f/2ff3cfe7d91ac3762100e511c4eff0c98824970d7c27e18e88c44a4d4567/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4c3849992e33e47403c2517d464564e4b4cf6a080ad761141504e271ab2c7cd", size = 487268, upload-time = "2025-10-13T06:33:13.784Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/b2/fbc05eef045124a9e5820812ddd641ec42add5e52f12126a85d942b0f166/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0c5ae335b1917aa101d7cfe1aacbedf3b54f489d2038e94c8f42ffe5bd304a", size = 474032, upload-time = "2025-12-02T16:58:00.344Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/d7/a95fbdd26f20b7bd5781bb5a4c51616fdd59f1c521010f668ffd54e59f5d/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:016f51409ed3d87c33ca5a31dd6766e75a809909e418a0ffd2965e0ae7b84a7b", size = 365853, upload-time = "2025-10-13T06:33:23.415Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a8/1472a5d5f849803fb2ad566ae43db8e5c9f3b1686b104dda245e4acfd963/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21d145e9fef6e2e53fdf17f9b6ab7e7fbba26064365c56d2103a41e95053d1d4", size = 365233, upload-time = "2025-12-02T16:58:08.102Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/7a/5d50d0b1167c79a509957d58a6bf9f6450f894e0bc233987cb85ccaec50f/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94dd711ea2ba76e664dab3e7f7b08cb5517cf5164fd94a552598acfd1f6df59a", size = 343697, upload-time = "2025-10-13T06:33:32.542Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/c8/ea700f277cba380c7919136a16e03f9f990f29da34c5404b861fbb8b6fd5/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ed5d7afd8b6b244c33316cc292122f26c20bf9677907bc5790c1b053097aff4", size = 348452, upload-time = "2025-12-02T16:58:15.863Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/89/85acbd412a5c7ef39ee5a96f5be28d6d38bce2c4521a264c747361b4c021/sqlglotrs-0.7.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:517198977f3baece667513326e42545b00b2878719922c58fcbfa21553f1338d", size = 363446, upload-time = "2025-10-13T06:33:03.995Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/f7/ba63c7cabcd71abed855e7a4cecb4b0df297bf17d315ff39eacf94926378/sqlglotrs-0.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:185442ad85a125719bf365a238c2b357c079cb5a13392adbbde172b1a0073410", size = 371656, upload-time = "2025-12-02T16:57:51.329Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/4d/0a04f29731b6fda327bd11495c143ce70d1a7446b22440a32d8571408a06/sqlglotrs-0.7.3-cp310-cp310-win32.whl", hash = "sha256:1e9121ef3a64dc7d18e500e5e93df458a9bb6f4111b8f8569d5e4f8db21e61d2", size = 183997, upload-time = "2025-10-13T06:33:58.579Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/dc/1ba05670afe7f4c7e651f972f4738dc4508525bb67b9151cdf463b0ef55b/sqlglotrs-0.8.0-cp310-cp310-win32.whl", hash = "sha256:a7d3f36d9c53090842ae18de6d96bd7634d73584255014983aad998f2b7dc95f", size = 188554, upload-time = "2025-12-02T16:58:39.078Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/16/0e95fa77409da059c951c6be11d4d73311c60bb5ed82f1d40a4afc9a1aa9/sqlglotrs-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:48fd7e9efef56331e1ef7402b6d65113c087da1cfe2ef80d143ee62046d49056", size = 195923, upload-time = "2025-10-13T06:34:06.676Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/fc/a393a837a9e09411da87cf8ee2d9f190e3bad37d289cd385e3791356a788/sqlglotrs-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:c8a5e3c8870323666e9695be7cc65f710ed437ceea572e69e2b14e63b70f21b2", size = 200973, upload-time = "2025-12-02T16:58:46.02Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/41/fcd87de298b562947cb2592feb9df5794886a8fa24eab8a080a552aa0e4d/sqlglotrs-0.7.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f2144fc8e472de2b165665c1370e7f0ca7f9400f60ca5e78c7aedbb3233bc8d7", size = 314465, upload-time = "2025-10-13T06:33:50.219Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/be/a6a8e41e59813663baf02b23534d822b62521d018ee740f132b4547c4239/sqlglotrs-0.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0267b0121073669d1184bc0441779559e6b0c6067a12571b63befa2a9b4b0f77", size = 318016, upload-time = "2025-12-02T16:58:32.555Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/81/22cf241e22f364c414d57893fad9cfea869f8866189e75575a3862f1d329/sqlglotrs-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93cb74928b205da3f29f2b9c728d2c6656ad30e1ef500560f6c851bca2129fbc", size = 300129, upload-time = "2025-10-13T06:33:42.205Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/02/bf65a608b2caf268d81073171196f93beed8d32731ebda1288153dec2b73/sqlglotrs-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c1a2fa22a3ae4b38c7df9abbf14b2473f7e71c859c95bc270bd4a169688380", size = 306527, upload-time = "2025-12-02T16:58:24.853Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/90/4e4220f8605c6fbca77dfad2052cdebf195099c99fd0684723677dcbf091/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a918137bacfa31529802063e349a99d5352f74c914beceb14689cd2864c5e4d0", size = 332735, upload-time = "2025-10-13T06:32:48.095Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/98/32de2ad5ea9310e220baabfb6b2ee1e3c7ebb3b83a1db9bd2acdf72de6a5/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7df3d2117c92004aa20082d71fbbd1735f063f123354d32d0b2b602ab4e1353", size = 341821, upload-time = "2025-12-02T16:57:34.854Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/35/abe3cb6aa197b5193fcb446ab69465b5927e09e281b2c05f4e12249fd335/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3fd0edbd957d136c67919ead10c90d832da1aedbbedc6da899d173fe78bf600", size = 342779, upload-time = "2025-10-13T06:32:56.782Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/99/64247cb3b9f99ca09aafa11791fe250326d498b194795af91cc957003852/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ecd7fdfd1be44828a8a8046ee743ffbaf93a972d7a125ff13e4673bb659fcf2c", size = 350003, upload-time = "2025-12-02T16:57:42.659Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/71/670ad31f4dbfe594592a1992c4e7a62003dc47dffb15d96b2fec4137f933/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a361a1dd8c55fbc57f81db738658141cab723509cc1b3edcc871bccfbba0cfb", size = 487344, upload-time = "2025-10-13T06:33:15.095Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/91/bc15e4d2322cc28f4f94e519b2ae927ba42844830efaacf973ff774d8e06/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:171df6454f3dc064b89895c51cfb713163188493b36b845bf7c17df0e5702095", size = 474163, upload-time = "2025-12-02T16:58:01.554Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/73/86e46b762b615c7cdec489e4b0670d2a04ea6fab0c0be30a5756e95f108f/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c698af6379475c243a8f190845bf1d1267a2c9867011a4567d5cfdcc5b0eb094", size = 366062, upload-time = "2025-10-13T06:33:25.183Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/8e/736451fc39f68f1e394a90d768dd9c8135412669ea3460e47033308cbb2e/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:497472ed07445a693e2699fd6f1b8ed5b8320488ade6a4a8e476664ee93ea51c", size = 365088, upload-time = "2025-12-02T16:58:09.604Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/07/b4dd7315df7d975c4b82d09106eb73ea2ee8f3734f764889913636e9d68c/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d63ed29058c56f153912c90811d8af1706d81f0c759883baeb21acb6322969", size = 343642, upload-time = "2025-10-13T06:33:33.826Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/2c/214f352fe03652b08873dcb8f4e6799a02be71446bdf9fea99ce13a502f3/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2be9add4daed501e28564208b30d4a772dfd6aaa1ad10dadd2d49f4e851f9fa", size = 348368, upload-time = "2025-12-02T16:58:17.363Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/84/2e834fc665236ef6b0fced14d75c8e9eb0db471d96fde539d8c37ce3a10f/sqlglotrs-0.7.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e19dee6dc46c4d84c556ae456fa0c6400edb157528fd369670b3d041b54ef21", size = 363731, upload-time = "2025-10-13T06:33:05.913Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/22/c445428a52d053a6f6b31858ac817afb997316e9f0ab2ee3187a10bd85a4/sqlglotrs-0.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:871d5ee6414f2d7116b670d0430c16f5b3d5a96480c274f7f3d50d97dbea7601", size = 371720, upload-time = "2025-12-02T16:57:52.71Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/db/b7063b1240a1c39bc5627880dbb80c9e3f7b5548a17962d3a6bf98239171/sqlglotrs-0.7.3-cp311-cp311-win32.whl", hash = "sha256:f1276d0f02eaefbdd149b614f6c21fb9be372d7e1137f19c3d5f9e50662367b3", size = 183607, upload-time = "2025-10-13T06:33:59.858Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/b2/301261db4ac543891f897b58a036e87ff33158ea4eda050ee0e08ae0083a/sqlglotrs-0.8.0-cp311-cp311-win32.whl", hash = "sha256:1bbe94effd9d64a8bdca12e0f14b28388059cb5a381561bac07aafedc8c63761", size = 188284, upload-time = "2025-12-02T16:58:40.21Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/98/e9cb2b3dd4abb34d2ae71747f113bf12f741a86fa29e661f1f09ba8376d0/sqlglotrs-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:ccf05fc6e89523cf5819982fab12b8fe07a9656dbb5356fc4b56b562e734c202", size = 196050, upload-time = "2025-10-13T06:34:07.921Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/a1/0534075d3b8a7c8ab8eff4ea7ba0338a2ef76e3d2e49105b189049430e99/sqlglotrs-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:05a5098ec2836799c4c43b06df7c68a2b4c19c0fce042a66706fe3edc957459d", size = 201117, upload-time = "2025-12-02T16:58:47.14Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/3f/3b059058e198b2fb6612d0ddaad5431a796d7081d40b21f12273ea1b26dc/sqlglotrs-0.7.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2e7be55bf719b5ebdc344a996b6d27b9a0ba9bae0a09462900805e2f7dc4dca5", size = 310987, upload-time = "2025-10-13T06:33:51.874Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/20/7beddfd545aaebbfee10a77ac8ef8a205ff597f9ce041c4b0437d0194392/sqlglotrs-0.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fcb53f27cf4b9cae8a66c5777b84eeb3d079e96bcb4277b627fd90bfd1a591b5", size = 314699, upload-time = "2025-12-02T16:58:33.82Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/b6/0058b2fe4f4813d9f3280d65ace97a637e8edd152be2a13bb1782c5c2eff/sqlglotrs-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6fef415993e1843201a57916f310b49e79669db379ff38094161fa93be2ffdf2", size = 296829, upload-time = "2025-10-13T06:33:43.838Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/6f/6223a1946fe24a979b8af3c7ae2d16c5451d8f35f2468782bd4af2c122da/sqlglotrs-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4da1480cc288e02bd459e4638f212fa86a1fef81eb2cd69e6fdbdeb64e3df729", size = 303385, upload-time = "2025-12-02T16:58:26.052Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/a8/35c593b03bf498876aea68ea944a7e7bb9cf648e68984f55795181c928dd/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e980354e576e852c53e0bb5444b04ebb6459054074bce8012cc3385dd3d116ed", size = 332313, upload-time = "2025-10-13T06:32:49.343Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/98/55050208ef839cad740df6ca86f2f3ca895d469f6ce2040cba32d0b6c4a0/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4a77df178b0ba242aba0e7cd775c3f9aef0fa79dfc31c6e642431ce690f51f", size = 341580, upload-time = "2025-12-02T16:57:36.197Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/bc/534e21a233846d33d6b55100485bf1844d301b0b75deded5310ef9cd171f/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1444b260c040cc80697956629f3fd3adece0bdb4f83bae22cd618ca3f18c4de8", size = 342309, upload-time = "2025-10-13T06:32:58.031Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/f2/6f1d207e629fd4810cc826cf419acc386f3d43d32987684730fbc2399503/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8647d20cc5a9ff39071786169b3f1acf56f266483fa55386111783bca335f04", size = 348451, upload-time = "2025-12-02T16:57:43.756Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/63/1d7bd7de87f01adb43cd1710d3fd5b9d5b0b3fea160bbeadc340fe1a9132/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3202c6f00145b8adb4632c1bb5071be5aa362829054653bac058dbcdbc6228e7", size = 484954, upload-time = "2025-10-13T06:33:16.697Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/1b/fa8a0907471fe7be3754bac683a21c984b17672eef6958206473f683b63a/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1afdd6a0fa915b3aef7c801cbdc815bb39b3d6aecc4d5b04c4ce54d3f73d0013", size = 475703, upload-time = "2025-12-02T16:58:02.843Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/bd/10126c9f59fb4f8fa51bf3f0ad17895b953bd09e1687986d5d9e110758c8/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17ae27e895f0ed960e28e76028c84758ff00df24e598654df3b5f22de8c7fc30", size = 366874, upload-time = "2025-10-13T06:33:26.888Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/56/f020c9c48d68883f6e24d69d18fe386eafc5963bc3982cc45013ec9b1ba0/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b4c1edeb80f572cf3586b9a23d15f18f48ac8dc481eceabdbb85dc7dbf8a2ce", size = 365842, upload-time = "2025-12-02T16:58:10.847Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/fa/f12a1eb9c22cdce962bafebefea58e898c19bae3d21e9b79d6e811a2951d/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a36c3d55b913c09dc31572ca7d5b423e85d761f1b3c9d8f86e2a1433a2f20d5", size = 342990, upload-time = "2025-10-13T06:33:35.478Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/7b/091464f8aa2232a2f33028f9c9a2cbea7c4e5719400656f203592d46264d/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b6d819f2753804d55b10e4320df08350cd2739556572a97ed1b1d7fc939f194", size = 348397, upload-time = "2025-12-02T16:58:18.567Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/1d/2bd1c8900d7a081a61a1c424785fd1a1452def751bc212630251423d80ce/sqlglotrs-0.7.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:94875611a2a953c06e8152b1d485f4d20ec61b72ebd848f96c04aca66b30f189", size = 362603, upload-time = "2025-10-13T06:33:07.507Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/1b/1b0cf0d41e8412786d1e80695778db799520223acf85c3ddc53c1200731f/sqlglotrs-0.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dcf2cce002969cefb1466f2837c716d20fc9eac62b05043523fda25b3de4c444", size = 369756, upload-time = "2025-12-02T16:57:53.85Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/3a/9c176a7f9b93d78168b3d137db4704a703cb51b32edb29d231b615130b47/sqlglotrs-0.7.3-cp312-cp312-win32.whl", hash = "sha256:64a708c87d1bea1f4bdb3edf0a3e94ea1b908ed229502da93621a4a50ef20189", size = 183180, upload-time = "2025-10-13T06:34:01.017Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/6e/d9e50472aa92736751abf3d6fcad1c793f0701f17a553ae787e4a7581a1d/sqlglotrs-0.8.0-cp312-cp312-win32.whl", hash = "sha256:5459235a25b30eae508bcaea8bc6ebc04610acd87e985ba4d602981a94078384", size = 187891, upload-time = "2025-12-02T16:58:41.57Z" },
|
||||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/ea/37757060d3caadb22509d349087e1e16a2dcc4c1649a00d2d6b501f8ff50/sqlglotrs-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:fe1ef1bedbb34b87dfb4e99a911026f8895ff2514b222cfd82cd08033406de2e", size = 195746, upload-time = "2025-10-13T06:34:09.478Z" },
|
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/a2/21d09ff2065a7e883f8f68dcea57fb23f6f04ba7a193f2ac2895b5dfafae/sqlglotrs-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:1e0de4fa8e6c54419bd63a1205f3218feb5e2649d72f1bc69c5261b6c333e63b", size = 200842, upload-time = "2025-12-02T16:58:48.181Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -10,6 +10,10 @@ interface DocPreviewerProps {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Word document preview component. Behavior:
|
||||||
|
// 1) Fetches the document as a Blob.
|
||||||
|
// 2) Detects .docx input via a ZIP header probe.
|
||||||
|
// 3) Renders .docx using Mammoth; presents a controlled "unsupported" notice for non-ZIP payloads.
|
||||||
export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
||||||
className,
|
className,
|
||||||
url,
|
url,
|
||||||
@ -17,6 +21,33 @@ export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
|||||||
const [htmlContent, setHtmlContent] = useState<string>('');
|
const [htmlContent, setHtmlContent] = useState<string>('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
// Determines whether the Blob represents a .docx document by checking for the ZIP
|
||||||
|
// file signature ("PK") in the initial bytes. A valid .docx file is a ZIP container
|
||||||
|
// and always begins with:
|
||||||
|
// 50 4B 03 04 ("PK..")
|
||||||
|
//
|
||||||
|
// Legacy .doc files use the CFBF binary format, commonly starting with:
|
||||||
|
// D0 CF 11 E0 A1 B1 1A E1
|
||||||
|
//
|
||||||
|
// Note that some files distributed with a “.doc” extension may internally be .docx
|
||||||
|
// documents (e.g., renamed files or files produced by systems that export .docx
|
||||||
|
// content under a .doc filename). These files will still present the ZIP signature
|
||||||
|
// and are therefore treated as supported .docx payloads. The header inspection
|
||||||
|
// ensures correct routing regardless of filename or reported extension.
|
||||||
|
const isZipLikeBlob = async (blob: Blob): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const headerSlice = blob.slice(0, 4);
|
||||||
|
const buf = await headerSlice.arrayBuffer();
|
||||||
|
const bytes = new Uint8Array(buf);
|
||||||
|
|
||||||
|
// ZIP files start with "PK" (0x50, 0x4B)
|
||||||
|
return bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to inspect blob header', e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchDocument = async () => {
|
const fetchDocument = async () => {
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
|
|
||||||
@ -36,24 +67,21 @@ export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
|||||||
const contentType: string =
|
const contentType: string =
|
||||||
blob.type || (res as any).headers?.['content-type'] || '';
|
blob.type || (res as any).headers?.['content-type'] || '';
|
||||||
|
|
||||||
// ---- Detect legacy .doc via MIME or URL ----
|
// Execution path selection: ZIP-like payloads are treated as .docx and rendered via Mammoth;
|
||||||
const cleanUrl = url.split(/[?#]/)[0].toLowerCase();
|
// non-ZIP payloads receive an explicit unsupported notice.
|
||||||
const isDocMime = /application\/msword/i.test(contentType);
|
const looksLikeZip = await isZipLikeBlob(blob);
|
||||||
const isLegacyDocByUrl =
|
|
||||||
cleanUrl.endsWith('.doc') && !cleanUrl.endsWith('.docx');
|
|
||||||
const isLegacyDoc = isDocMime || isLegacyDocByUrl;
|
|
||||||
|
|
||||||
if (isLegacyDoc) {
|
if (!looksLikeZip) {
|
||||||
// Do not call mammoth and do not throw an error; instead, show a note in the preview area
|
// Non-ZIP payload (likely legacy .doc or another format): skip Mammoth processing.
|
||||||
setHtmlContent(`
|
setHtmlContent(`
|
||||||
<div class="flex h-full items-center justify-center">
|
<div class="flex h-full items-center justify-center">
|
||||||
<div class="border border-dashed border-border-normal rounded-xl p-8 max-w-2xl text-center">
|
<div class="border border-dashed border-border-normal rounded-xl p-8 max-w-2xl text-center">
|
||||||
<p class="text-2xl font-bold mb-4">
|
<p class="text-2xl font-bold mb-4">
|
||||||
Preview not available for .doc files
|
Preview is not available for this Word document
|
||||||
</p>
|
</p>
|
||||||
<p class="italic text-sm text-muted-foreground leading-relaxed">
|
<p class="italic text-sm text-muted-foreground leading-relaxed">
|
||||||
Mammoth does not support <code>.doc</code> documents.<br/>
|
Mammoth supports modern <code>.docx</code> files only.<br/>
|
||||||
Inline preview is unavailable.
|
The file header does not indicate a <code>.docx</code> ZIP archive.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,7 +89,7 @@ export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Standard .docx preview path ----
|
// ZIP-like payload: parse as .docx with Mammoth
|
||||||
const arrayBuffer = await blob.arrayBuffer();
|
const arrayBuffer = await blob.arrayBuffer();
|
||||||
const result = await mammoth.convertToHtml(
|
const result = await mammoth.convertToHtml(
|
||||||
{ arrayBuffer },
|
{ arrayBuffer },
|
||||||
@ -74,8 +102,7 @@ export const DocPreviewer: React.FC<DocPreviewerProps> = ({
|
|||||||
|
|
||||||
setHtmlContent(styledContent);
|
setHtmlContent(styledContent);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Only errors from the mammoth conversion path should surface here
|
message.error('Failed to parse document.');
|
||||||
message.error('Document parsing failed');
|
|
||||||
console.error('Error parsing document:', err);
|
console.error('Error parsing document:', err);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import './css/cloud9_night.less';
|
import './css/cloud9_night.less';
|
||||||
import './css/index.less';
|
import './css/index.less';
|
||||||
import { JsonEditorOptions, JsonEditorProps } from './interface';
|
import { JsonEditorOptions, JsonEditorProps } from './interface';
|
||||||
|
|
||||||
const defaultConfig: JsonEditorOptions = {
|
const defaultConfig: JsonEditorOptions = {
|
||||||
mode: 'code',
|
mode: 'code',
|
||||||
modes: ['tree', 'code'],
|
modes: ['tree', 'code'],
|
||||||
@ -14,6 +15,7 @@ const defaultConfig: JsonEditorOptions = {
|
|||||||
enableTransform: false,
|
enableTransform: false,
|
||||||
indentation: 2,
|
indentation: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const JsonEditor: React.FC<JsonEditorProps> = ({
|
const JsonEditor: React.FC<JsonEditorProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@ -25,43 +27,62 @@ const JsonEditor: React.FC<JsonEditorProps> = ({
|
|||||||
const editorRef = useRef<any>(null);
|
const editorRef = useRef<any>(null);
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const currentLanguageRef = useRef<string>(i18n.language);
|
const currentLanguageRef = useRef<string>(i18n.language);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== 'undefined') {
|
let isMounted = true;
|
||||||
const JSONEditor = require('jsoneditor');
|
|
||||||
import('jsoneditor/dist/jsoneditor.min.css');
|
|
||||||
|
|
||||||
if (containerRef.current) {
|
const initEditor = async () => {
|
||||||
// Default configuration options
|
if (typeof window !== 'undefined') {
|
||||||
const defaultOptions: JsonEditorOptions = {
|
try {
|
||||||
...defaultConfig,
|
const JSONEditorModule = await import('jsoneditor');
|
||||||
language: i18n.language === 'zh' ? 'zh-CN' : 'en',
|
const JSONEditor = JSONEditorModule.default || JSONEditorModule;
|
||||||
onChange: () => {
|
|
||||||
if (editorRef.current && onChange) {
|
await import('jsoneditor/dist/jsoneditor.min.css');
|
||||||
try {
|
|
||||||
const updatedJson = editorRef.current.get();
|
if (isMounted && containerRef.current) {
|
||||||
onChange(updatedJson);
|
// Default configuration options
|
||||||
} catch (err) {
|
const defaultOptions: JsonEditorOptions = {
|
||||||
// Do not trigger onChange when parsing error occurs
|
...defaultConfig,
|
||||||
console.error(err);
|
language: i18n.language === 'zh' ? 'zh-CN' : 'en',
|
||||||
}
|
onChange: () => {
|
||||||
|
if (editorRef.current && onChange) {
|
||||||
|
try {
|
||||||
|
const updatedJson = editorRef.current.get();
|
||||||
|
onChange(updatedJson);
|
||||||
|
} catch (err) {
|
||||||
|
// Do not trigger onChange when parsing error occurs
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...options, // Merge user provided options with defaults
|
||||||
|
};
|
||||||
|
|
||||||
|
editorRef.current = new JSONEditor(
|
||||||
|
containerRef.current,
|
||||||
|
defaultOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
editorRef.current.set(value);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
...options, // Merge user provided options with defaults
|
|
||||||
};
|
|
||||||
|
|
||||||
editorRef.current = new JSONEditor(
|
setIsLoading(false);
|
||||||
containerRef.current,
|
}
|
||||||
defaultOptions,
|
} catch (error) {
|
||||||
);
|
console.error('Failed to load jsoneditor:', error);
|
||||||
|
if (isMounted) {
|
||||||
if (value) {
|
setIsLoading(false);
|
||||||
editorRef.current.set(value);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
initEditor();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
if (typeof editorRef.current.destroy === 'function') {
|
if (typeof editorRef.current.destroy === 'function') {
|
||||||
editorRef.current.destroy();
|
editorRef.current.destroy();
|
||||||
@ -92,26 +113,38 @@ const JsonEditor: React.FC<JsonEditorProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recreate the editor with new language
|
// Recreate the editor with new language
|
||||||
const JSONEditor = require('jsoneditor');
|
const initEditorWithNewLanguage = async () => {
|
||||||
|
try {
|
||||||
|
const JSONEditorModule = await import('jsoneditor');
|
||||||
|
const JSONEditor = JSONEditorModule.default || JSONEditorModule;
|
||||||
|
|
||||||
const newOptions: JsonEditorOptions = {
|
const newOptions: JsonEditorOptions = {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
language: i18n.language === 'zh' ? 'zh-CN' : 'en',
|
language: i18n.language === 'zh' ? 'zh-CN' : 'en',
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
if (editorRef.current && onChange) {
|
if (editorRef.current && onChange) {
|
||||||
try {
|
try {
|
||||||
const updatedJson = editorRef.current.get();
|
const updatedJson = editorRef.current.get();
|
||||||
onChange(updatedJson);
|
onChange(updatedJson);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Do not trigger onChange when parsing error occurs
|
// Do not trigger onChange when parsing error occurs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...options, // Merge user provided options with defaults
|
...options, // Merge user provided options with defaults
|
||||||
|
};
|
||||||
|
|
||||||
|
editorRef.current = new JSONEditor(containerRef.current, newOptions);
|
||||||
|
editorRef.current.set(currentData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'Failed to reload jsoneditor with new language:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
editorRef.current = new JSONEditor(containerRef.current, newOptions);
|
initEditorWithNewLanguage();
|
||||||
editorRef.current.set(currentData);
|
|
||||||
}
|
}
|
||||||
}, [i18n.language, value, onChange, options]);
|
}, [i18n.language, value, onChange, options]);
|
||||||
|
|
||||||
@ -135,7 +168,13 @@ const JsonEditor: React.FC<JsonEditorProps> = ({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{ height }}
|
style={{ height }}
|
||||||
className={`ace-tomorrow-night w-full border border-border-button rounded-lg overflow-hidden bg-bg-input ${className} `}
|
className={`ace-tomorrow-night w-full border border-border-button rounded-lg overflow-hidden bg-bg-input ${className} `}
|
||||||
/>
|
>
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="text-text-secondary">Loading editor...</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
6
web/src/custom.d.ts
vendored
6
web/src/custom.d.ts
vendored
@ -2,3 +2,9 @@ declare module '*.md' {
|
|||||||
const content: string;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'jsoneditor' {
|
||||||
|
const JSONEditor: any;
|
||||||
|
export default JSONEditor;
|
||||||
|
export = JSONEditor;
|
||||||
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import { useDropdownManager } from './context';
|
|||||||
|
|
||||||
import { AgentBackground } from '@/components/canvas/background';
|
import { AgentBackground } from '@/components/canvas/background';
|
||||||
import Spotlight from '@/components/spotlight';
|
import Spotlight from '@/components/spotlight';
|
||||||
|
import { useNodeLoading } from '../hooks/use-node-loading';
|
||||||
import {
|
import {
|
||||||
useHideFormSheetOnNodeDeletion,
|
useHideFormSheetOnNodeDeletion,
|
||||||
useShowDrawer,
|
useShowDrawer,
|
||||||
@ -166,6 +167,8 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
});
|
});
|
||||||
const [lastSendLoading, setLastSendLoading] = useState(false);
|
const [lastSendLoading, setLastSendLoading] = useState(false);
|
||||||
|
|
||||||
|
const [currentSendLoading, setCurrentSendLoading] = useState(false);
|
||||||
|
|
||||||
const { handleBeforeDelete } = useBeforeDelete();
|
const { handleBeforeDelete } = useBeforeDelete();
|
||||||
|
|
||||||
const { addCanvasNode, addNoteNode } = useAddNode(reactFlowInstance);
|
const { addCanvasNode, addNoteNode } = useAddNode(reactFlowInstance);
|
||||||
@ -182,6 +185,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
}, [chatVisible, clearEventList, currentTaskId, stopMessage]);
|
}, [chatVisible, clearEventList, currentTaskId, stopMessage]);
|
||||||
|
|
||||||
const setLastSendLoadingFunc = (loading: boolean, messageId: string) => {
|
const setLastSendLoadingFunc = (loading: boolean, messageId: string) => {
|
||||||
|
setCurrentSendLoading(!!loading);
|
||||||
if (messageId === currentMessageId) {
|
if (messageId === currentMessageId) {
|
||||||
setLastSendLoading(loading);
|
setLastSendLoading(loading);
|
||||||
} else {
|
} else {
|
||||||
@ -249,7 +253,10 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
clearActiveDropdown,
|
clearActiveDropdown,
|
||||||
removePlaceholderNode,
|
removePlaceholderNode,
|
||||||
]);
|
]);
|
||||||
|
const { lastNode, setDerivedMessages, startButNotFinishedNodeIds } =
|
||||||
|
useNodeLoading({
|
||||||
|
currentEventListWithoutMessageById,
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div className={cn(styles.canvasWrapper, 'px-5 pb-5')}>
|
<div className={cn(styles.canvasWrapper, 'px-5 pb-5')}>
|
||||||
<svg
|
<svg
|
||||||
@ -285,7 +292,15 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
</marker>
|
</marker>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<AgentInstanceContext.Provider value={{ addCanvasNode, showFormDrawer }}>
|
<AgentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
addCanvasNode,
|
||||||
|
showFormDrawer,
|
||||||
|
lastNode,
|
||||||
|
currentSendLoading,
|
||||||
|
startButNotFinishedNodeIds,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
connectionMode={ConnectionMode.Loose}
|
connectionMode={ConnectionMode.Loose}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
@ -380,9 +395,10 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
></FormSheet>
|
></FormSheet>
|
||||||
</AgentInstanceContext.Provider>
|
</AgentInstanceContext.Provider>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{chatVisible && (
|
{chatVisible && (
|
||||||
<AgentChatContext.Provider
|
<AgentChatContext.Provider
|
||||||
value={{ showLogSheet, setLastSendLoadingFunc }}
|
value={{ showLogSheet, setLastSendLoadingFunc, setDerivedMessages }}
|
||||||
>
|
>
|
||||||
<AgentChatLogContext.Provider
|
<AgentChatLogContext.Provider
|
||||||
value={{ addEventList, setCurrentMessageId }}
|
value={{ addEventList, setCurrentMessageId }}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ function InnerAgentNode({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
<ToolBar selected={selected} id={id} label={data.label}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
{isHeadAgent && (
|
{isHeadAgent && (
|
||||||
<>
|
<>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) {
|
|||||||
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {});
|
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
type="source"
|
type="source"
|
||||||
position={Position.Right}
|
position={Position.Right}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export function InnerCategorizeNode({
|
|||||||
const { positions } = useBuildCategorizeHandlePositions({ data, id });
|
const { positions } = useBuildCategorizeHandlePositions({ data, id });
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
<ToolBar selected={selected} id={id} label={data.label}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
|
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export function ExitLoopNode({ id, data, selected }: NodeProps<BaseNode<any>>) {
|
|||||||
showRun={false}
|
showRun={false}
|
||||||
showCopy={false}
|
showCopy={false}
|
||||||
>
|
>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
</NodeWrapper>
|
</NodeWrapper>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ function InnerFileNode({ data, id, selected }: NodeProps<IBeginNode>) {
|
|||||||
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {});
|
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
type="source"
|
type="source"
|
||||||
position={Position.Right}
|
position={Position.Right}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ function InnerRagNode({
|
|||||||
showRun={needsSingleStepDebugging(data.label)}
|
showRun={needsSingleStepDebugging(data.label)}
|
||||||
showCopy={showCopyIcon(data.label)}
|
showCopy={showCopyIcon(data.label)}
|
||||||
>
|
>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
type="source"
|
type="source"
|
||||||
|
|||||||
@ -16,7 +16,7 @@ function InnerMessageNode({ id, data, selected }: NodeProps<IMessageNode>) {
|
|||||||
const messages: string[] = get(data, 'form.content', []);
|
const messages: string[] = get(data, 'form.content', []);
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
<ToolBar selected={selected} id={id} label={data.label}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
<NodeHeader
|
<NodeHeader
|
||||||
id={id}
|
id={id}
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { HTMLAttributes } from 'react';
|
import { Loader } from 'lucide-react';
|
||||||
|
import { HTMLAttributes, useContext } from 'react';
|
||||||
|
import { AgentInstanceContext } from '../../context';
|
||||||
|
|
||||||
type IProps = HTMLAttributes<HTMLDivElement> & { selected?: boolean };
|
type IProps = HTMLAttributes<HTMLDivElement> & { selected?: boolean };
|
||||||
|
|
||||||
export function NodeWrapper({ children, className, selected }: IProps) {
|
export function NodeWrapper({ children, className, selected, id }: IProps) {
|
||||||
|
const { currentSendLoading, startButNotFinishedNodeIds = [] } =
|
||||||
|
useContext(AgentInstanceContext);
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -12,6 +16,13 @@ export function NodeWrapper({ children, className, selected }: IProps) {
|
|||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
{id &&
|
||||||
|
startButNotFinishedNodeIds.indexOf(id as string) > -1 &&
|
||||||
|
currentSendLoading && (
|
||||||
|
<div className=" absolute right-0 left-0 top-0 flex items-start justify-end p-2">
|
||||||
|
<Loader size={12} className=" animate-spin" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{children}
|
{children}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -19,7 +19,7 @@ function ParserNode({
|
|||||||
}: NodeProps<BaseNode<ParserFormSchemaType>>) {
|
}: NodeProps<BaseNode<ParserFormSchemaType>>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
type="target"
|
type="target"
|
||||||
|
|||||||
@ -27,7 +27,7 @@ function InnerRetrievalNode({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
<ToolBar selected={selected} id={id} label={data.label}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.Start}
|
id={NodeHandleId.Start}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ function InnerSplitterNode({
|
|||||||
showCopy={false}
|
showCopy={false}
|
||||||
showRun={false}
|
showRun={false}
|
||||||
>
|
>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
type="target"
|
type="target"
|
||||||
|
|||||||
@ -65,7 +65,7 @@ function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) {
|
|||||||
const { positions } = useBuildSwitchHandlePositions({ data, id });
|
const { positions } = useBuildSwitchHandlePositions({ data, id });
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label} showRun={false}>
|
<ToolBar selected={selected} id={id} label={data.label} showRun={false}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<LeftEndHandle></LeftEndHandle>
|
<LeftEndHandle></LeftEndHandle>
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
<section className="gap-2.5 flex flex-col">
|
<section className="gap-2.5 flex flex-col">
|
||||||
|
|||||||
@ -27,7 +27,7 @@ function TokenizerNode({
|
|||||||
showRun={false}
|
showRun={false}
|
||||||
showCopy={false}
|
showCopy={false}
|
||||||
>
|
>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
type="target"
|
type="target"
|
||||||
|
|||||||
@ -44,7 +44,7 @@ function InnerToolNode({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected} id={id}>
|
||||||
<Handle
|
<Handle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
type="target"
|
type="target"
|
||||||
|
|||||||
@ -13,8 +13,9 @@ import {
|
|||||||
} from '@/hooks/use-agent-request';
|
} from '@/hooks/use-agent-request';
|
||||||
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
||||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback, useContext } from 'react';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
|
import { AgentChatContext } from '../context';
|
||||||
import DebugContent from '../debug-content';
|
import DebugContent from '../debug-content';
|
||||||
import { useAwaitCompentData } from '../hooks/use-chat-logic';
|
import { useAwaitCompentData } from '../hooks/use-chat-logic';
|
||||||
import { useIsTaskMode } from '../hooks/use-get-begin-query';
|
import { useIsTaskMode } from '../hooks/use-get-begin-query';
|
||||||
@ -49,6 +50,9 @@ function AgentChatBox() {
|
|||||||
canvasId: canvasId as string,
|
canvasId: canvasId as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { setDerivedMessages } = useContext(AgentChatContext);
|
||||||
|
setDerivedMessages?.(derivedMessages);
|
||||||
|
|
||||||
const isTaskMode = useIsTaskMode();
|
const isTaskMode = useIsTaskMode();
|
||||||
|
|
||||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
import { INodeEvent } from '@/hooks/use-send-message';
|
||||||
|
import { IMessage } from '@/interfaces/database/chat';
|
||||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||||
import { HandleType, Position } from '@xyflow/react';
|
import { HandleType, Position } from '@xyflow/react';
|
||||||
import { createContext } from 'react';
|
import { Dispatch, SetStateAction, createContext } from 'react';
|
||||||
import { useAddNode } from './hooks/use-add-node';
|
import { useAddNode } from './hooks/use-add-node';
|
||||||
import { useCacheChatLog } from './hooks/use-cache-chat-log';
|
import { useCacheChatLog } from './hooks/use-cache-chat-log';
|
||||||
import { useShowFormDrawer, useShowLogSheet } from './hooks/use-show-drawer';
|
import { useShowFormDrawer, useShowLogSheet } from './hooks/use-show-drawer';
|
||||||
@ -13,7 +15,11 @@ type AgentInstanceContextType = Pick<
|
|||||||
ReturnType<typeof useAddNode>,
|
ReturnType<typeof useAddNode>,
|
||||||
'addCanvasNode'
|
'addCanvasNode'
|
||||||
> &
|
> &
|
||||||
Pick<ReturnType<typeof useShowFormDrawer>, 'showFormDrawer'>;
|
Pick<ReturnType<typeof useShowFormDrawer>, 'showFormDrawer'> & {
|
||||||
|
lastNode: INodeEvent | null;
|
||||||
|
currentSendLoading: boolean;
|
||||||
|
startButNotFinishedNodeIds: string[];
|
||||||
|
};
|
||||||
|
|
||||||
export const AgentInstanceContext = createContext<AgentInstanceContextType>(
|
export const AgentInstanceContext = createContext<AgentInstanceContextType>(
|
||||||
{} as AgentInstanceContextType,
|
{} as AgentInstanceContextType,
|
||||||
@ -22,7 +28,10 @@ export const AgentInstanceContext = createContext<AgentInstanceContextType>(
|
|||||||
type AgentChatContextType = Pick<
|
type AgentChatContextType = Pick<
|
||||||
ReturnType<typeof useShowLogSheet>,
|
ReturnType<typeof useShowLogSheet>,
|
||||||
'showLogSheet'
|
'showLogSheet'
|
||||||
> & { setLastSendLoadingFunc: (loading: boolean, messageId: string) => void };
|
> & {
|
||||||
|
setLastSendLoadingFunc: (loading: boolean, messageId: string) => void;
|
||||||
|
setDerivedMessages: Dispatch<SetStateAction<IMessage[] | undefined>>;
|
||||||
|
};
|
||||||
|
|
||||||
export const AgentChatContext = createContext<AgentChatContextType>(
|
export const AgentChatContext = createContext<AgentChatContextType>(
|
||||||
{} as AgentChatContextType,
|
{} as AgentChatContextType,
|
||||||
|
|||||||
@ -55,7 +55,7 @@ const FormSheet = ({
|
|||||||
<Sheet open={visible} modal={false}>
|
<Sheet open={visible} modal={false}>
|
||||||
<SheetContent
|
<SheetContent
|
||||||
className={cn('top-20 p-0 flex flex-col pb-20', {
|
className={cn('top-20 p-0 flex flex-col pb-20', {
|
||||||
'right-[620px]': chatVisible,
|
'right-[clamp(0px,34%,620px)]': chatVisible,
|
||||||
})}
|
})}
|
||||||
closeIcon={false}
|
closeIcon={false}
|
||||||
>
|
>
|
||||||
|
|||||||
88
web/src/pages/agent/hooks/use-node-loading.ts
Normal file
88
web/src/pages/agent/hooks/use-node-loading.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
INodeData,
|
||||||
|
INodeEvent,
|
||||||
|
MessageEventType,
|
||||||
|
} from '@/hooks/use-send-message';
|
||||||
|
import { IMessage } from '@/interfaces/database/chat';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
export const useNodeLoading = ({
|
||||||
|
currentEventListWithoutMessageById,
|
||||||
|
}: {
|
||||||
|
currentEventListWithoutMessageById: (messageId: string) => INodeEvent[];
|
||||||
|
}) => {
|
||||||
|
const [derivedMessages, setDerivedMessages] = useState<IMessage[]>();
|
||||||
|
|
||||||
|
const lastMessageId = useMemo(() => {
|
||||||
|
return derivedMessages?.[derivedMessages?.length - 1]?.id;
|
||||||
|
}, [derivedMessages]);
|
||||||
|
|
||||||
|
const currentEventListWithoutMessage = useMemo(() => {
|
||||||
|
if (!lastMessageId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return currentEventListWithoutMessageById(lastMessageId);
|
||||||
|
}, [currentEventListWithoutMessageById, lastMessageId]);
|
||||||
|
|
||||||
|
const startedNodeList = useMemo(() => {
|
||||||
|
const duplicateList = currentEventListWithoutMessage?.filter(
|
||||||
|
(x) => x.event === MessageEventType.NodeStarted,
|
||||||
|
) as INodeEvent[];
|
||||||
|
|
||||||
|
// Remove duplicate nodes
|
||||||
|
return duplicateList?.reduce<Array<INodeEvent>>((pre, cur) => {
|
||||||
|
if (pre.every((x) => x.data.component_id !== cur.data.component_id)) {
|
||||||
|
pre.push(cur);
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
}, [currentEventListWithoutMessage]);
|
||||||
|
|
||||||
|
const filterFinishedNodeList = useCallback(() => {
|
||||||
|
const nodeEventList = currentEventListWithoutMessage
|
||||||
|
.filter(
|
||||||
|
(x) => x.event === MessageEventType.NodeFinished,
|
||||||
|
// x.event === MessageEventType.NodeFinished &&
|
||||||
|
// (x.data as INodeData)?.component_id === componentId,
|
||||||
|
)
|
||||||
|
.map((x) => x.data);
|
||||||
|
|
||||||
|
return nodeEventList;
|
||||||
|
}, [currentEventListWithoutMessage]);
|
||||||
|
|
||||||
|
const lastNode = useMemo(() => {
|
||||||
|
if (!startedNodeList) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return startedNodeList[startedNodeList.length - 1];
|
||||||
|
}, [startedNodeList]);
|
||||||
|
|
||||||
|
const startNodeIds = useMemo(() => {
|
||||||
|
if (!startedNodeList) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return startedNodeList.map((x) => x.data.component_id);
|
||||||
|
}, [startedNodeList]);
|
||||||
|
|
||||||
|
const finishNodeIds = useMemo(() => {
|
||||||
|
if (!lastNode) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const nodeDataList = filterFinishedNodeList();
|
||||||
|
const finishNodeIdsTemp = nodeDataList.map(
|
||||||
|
(x: INodeData) => x.component_id,
|
||||||
|
);
|
||||||
|
return Array.from(new Set(finishNodeIdsTemp));
|
||||||
|
}, [lastNode, filterFinishedNodeList]);
|
||||||
|
|
||||||
|
const startButNotFinishedNodeIds = useMemo(() => {
|
||||||
|
return startNodeIds.filter((x) => !finishNodeIds.includes(x));
|
||||||
|
}, [finishNodeIds, startNodeIds]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastNode,
|
||||||
|
startButNotFinishedNodeIds,
|
||||||
|
filterFinishedNodeList,
|
||||||
|
setDerivedMessages,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -26,7 +26,7 @@ export function LogSheet({
|
|||||||
return (
|
return (
|
||||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||||
<SheetContent
|
<SheetContent
|
||||||
className={cn('top-20 right-[620px]')}
|
className={cn('top-20 right-[clamp(0px,34%,620px)]')}
|
||||||
onInteractOutside={(e) => e.preventDefault()}
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
|
|||||||
Reference in New Issue
Block a user